mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-22 10:08:45 +02:00
Event Service
This commit is contained in:
@@ -299,6 +299,7 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
|
||||
*
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @deprecated
|
||||
*/
|
||||
interface iApplicationUIExtension
|
||||
{
|
||||
@@ -441,6 +442,7 @@ interface iApplicationUIExtension
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @since 2.7.0
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
{
|
||||
@@ -1770,4 +1772,4 @@ class RestUtils
|
||||
interface iModuleExtension
|
||||
{
|
||||
public function __construct();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Service\EventName;
|
||||
|
||||
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
|
||||
|
||||
define('HILIGHT_CLASS_CRITICAL', 'red');
|
||||
@@ -4162,6 +4164,7 @@ EOF
|
||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||
}
|
||||
}
|
||||
$this->FireEvent(EventName::ON_CHECK_TO_WRITE, array('error_messages' => &$this->m_aCheckIssues));
|
||||
|
||||
// User rights
|
||||
//
|
||||
@@ -4210,6 +4213,7 @@ EOF
|
||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||
}
|
||||
}
|
||||
$this->FireEvent(EventName::ON_CHECK_TO_DELETE, array('error_messages' => &$this->m_aDeleteIssues));
|
||||
|
||||
// User rights
|
||||
//
|
||||
|
||||
@@ -56,6 +56,7 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
{
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
$sAuthUser = $this->GetAuthUser();
|
||||
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
|
||||
{
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
@@ -86,4 +87,4 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
/** @var string $sAuthUser */
|
||||
return $sAuthUser; // Retrieve the value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
{
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||
{
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
@@ -90,4 +91,4 @@ class LoginURL extends AbstractLoginFSMExtension
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Service\Event;
|
||||
use Combodo\iTop\Service\EventName;
|
||||
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
@@ -447,6 +450,7 @@ class LoginWebPage extends NiceWebPage
|
||||
$_SESSION['login_state'] = self::LOGIN_STATE_START;
|
||||
}
|
||||
$sLoginState = $_SESSION['login_state'];
|
||||
$bFireEvent = ($sLoginState != self::LOGIN_STATE_CONNECTED);
|
||||
|
||||
$sSessionLog = '';
|
||||
if ($bLoginDebug)
|
||||
@@ -487,10 +491,15 @@ class LoginWebPage extends NiceWebPage
|
||||
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
|
||||
if ($iResponse == self::LOGIN_FSM_RETURN)
|
||||
{
|
||||
if ($bFireEvent)
|
||||
{
|
||||
Event::FireEvent(EventName::LOGIN, null, array('code' => $iErrorCode, 'state' => $sLoginState));
|
||||
}
|
||||
return $iErrorCode; // Asked to exit FSM, generally login OK
|
||||
}
|
||||
if ($iResponse == self::LOGIN_FSM_ERROR)
|
||||
{
|
||||
Event::FireEvent(EventName::LOGIN, null, array('code' => $iErrorCode, 'state' => $sLoginState));
|
||||
$sLoginState = self::LOGIN_STATE_SET_ERROR; // Next state will be error
|
||||
// An error was detected, skip the other plugins turn
|
||||
break;
|
||||
@@ -504,6 +513,7 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
Event::FireEvent(EventName::LOGIN, null, array('state' => $_SESSION['login_state']));
|
||||
IssueLog::Error($e->getTraceAsString());
|
||||
static::ResetSession();
|
||||
die($e->getMessage());
|
||||
|
||||
@@ -254,5 +254,237 @@
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
<events _delta="define">
|
||||
<event id="ObjectDetails">
|
||||
<description>An object details is about to be displayed to a user</description>
|
||||
<available_filters>Class hierarchy of the displayed object</available_filters>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object displayed</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="BeforeInsert">
|
||||
<description>An object is about to be inserted in the database</description>
|
||||
<replaces>DBObject::OnInsert</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DBObjectKeyReady">
|
||||
<description>A key (id) has been generated for an object inserted into the database (use GetKey() to read the new key)</description>
|
||||
<replaces>DBObject::OnObjectKeyReady</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="AfterInsert">
|
||||
<description>An object has been inserted into the database</description>
|
||||
<replaces>DBObject::AfterInsert</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="BeforeUpdate">
|
||||
<description>An object is about to be updated in the database</description>
|
||||
<replaces>DBObject::OnUpdate</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object updated</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="AfterUpdate">
|
||||
<description>An object has been updated into the database</description>
|
||||
<replaces>DBObject::AfterUpdate</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object updated</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="BeforeDelete">
|
||||
<description>An object is about to be deleted in the database</description>
|
||||
<replaces>DBObject::OnDelete</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object deleted</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="AfterDelete">
|
||||
<description>An object has been deleted into the database</description>
|
||||
<replaces>DBObject::AfterDelete</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object deleted</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="BeforeApplyStimulus">
|
||||
<description>A stimulus is about to be applied to an object</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object where the stimulus is to be applied</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="AfterApplyStimulus">
|
||||
<description>A stimulus has been applied to an object</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object where the stimulus has been applied</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DBObjectLoaded">
|
||||
<description>An object has been loaded from the database</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object loaded</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DBObjectNew">
|
||||
<description>An object has been created in memory</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object created</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DBObjectReload">
|
||||
<description>An object has been re-loaded from the database</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object re-loaded</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="OnCheckToWrite">
|
||||
<description>Check an object before it is written into the database</description>
|
||||
<replaces>cmdbAbstractObject::DoCheckToWrite</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object to check</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="error_messages">
|
||||
<description>Array of strings where all the errors found during the object checking are added</description>
|
||||
<type>array</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DoCheckToDelete">
|
||||
<description>Check an object before it is deleted from the database</description>
|
||||
<replaces>cmdbAbstractObject::DoCheckToDelete</replaces>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object to check</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="error_messages">
|
||||
<description>Array of strings where all the errors found during the object checking are added</description>
|
||||
<type>array</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
<event id="DownloadDocument">
|
||||
<description>A document has been downloaded from the GUI</description>
|
||||
<arguments>
|
||||
<argument id="object">
|
||||
<description>The object containing the document</description>
|
||||
<type>DBObject</type>
|
||||
</argument>
|
||||
<argument id="document">
|
||||
<description>The document downloaded</description>
|
||||
<type>ormDocument</type>
|
||||
</argument>
|
||||
<argument id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</argument>
|
||||
</arguments>
|
||||
</event>
|
||||
</events>
|
||||
</meta>
|
||||
</itop_design>
|
||||
</itop_design>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Service\Event;
|
||||
use Combodo\iTop\Service\EventName;
|
||||
|
||||
/**
|
||||
* All objects to be displayed in the application (either as a list or as details)
|
||||
@@ -153,9 +155,14 @@ abstract class DBObject implements iDisplay
|
||||
protected $m_aSynchroData = null;
|
||||
protected $m_sHighlightCode = null;
|
||||
protected $m_aCallbacks = array();
|
||||
/**
|
||||
* @var string local events suffix
|
||||
*/
|
||||
protected $m_sEventUniqId = '';
|
||||
protected static $m_iEventUniqCounter = 0;
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* DBObject constructor.
|
||||
*
|
||||
* You should preferably use MetaModel::NewObject() instead of this constructor.
|
||||
@@ -179,6 +186,9 @@ abstract class DBObject implements iDisplay
|
||||
$this->m_bFullyLoaded = $this->IsFullyLoaded();
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
$this->m_sEventUniqId = 'DataModel_'.self::$m_iEventUniqCounter++;
|
||||
$this->RegisterEvents();
|
||||
$this->FireEvent(EventName::DB_OBJECT_LOADED);
|
||||
return;
|
||||
}
|
||||
// Creation of a brand new object
|
||||
@@ -205,6 +215,14 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->UpdateMetaAttributes();
|
||||
|
||||
$this->m_sEventUniqId = 'DataModel_'.self::$m_iEventUniqCounter++;
|
||||
$this->RegisterEvents();
|
||||
$this->FireEvent(EventName::DB_OBJECT_NEW);
|
||||
}
|
||||
|
||||
protected function RegisterEvents()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,6 +369,7 @@ abstract class DBObject implements iDisplay
|
||||
public function Reload($bAllowAllData = false)
|
||||
{
|
||||
assert($this->m_bIsInDB);
|
||||
$this->FireEvent(EventName::DB_OBJECT_RELOAD);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, true /* AllowAllData */);
|
||||
if (empty($aRow))
|
||||
{
|
||||
@@ -2687,6 +2706,7 @@ abstract class DBObject implements iDisplay
|
||||
// Ensure the update of the values (we are accessing the data directly)
|
||||
$this->DoComputeValues();
|
||||
$this->OnInsert();
|
||||
$this->FireEvent(EventName::BEFORE_INSERT);
|
||||
|
||||
if ($this->m_iKey < 0)
|
||||
{
|
||||
@@ -2759,6 +2779,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
$this->FireEvent(EventName::DB_OBJECT_KEY_READY);
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
@@ -2789,6 +2810,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->AfterInsert();
|
||||
$this->FireEvent(EventName::AFTER_INSERT);
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
@@ -3037,6 +3059,8 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
$this->OnUpdate();
|
||||
$this->FireEvent(EventName::BEFORE_UPDATE);
|
||||
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
@@ -3239,6 +3263,7 @@ abstract class DBObject implements iDisplay
|
||||
try
|
||||
{
|
||||
$this->AfterUpdate();
|
||||
$this->FireEvent(EventName::AFTER_UPDATE);
|
||||
|
||||
// Reload to get the external attributes
|
||||
if ($bHasANewExternalKeyValue)
|
||||
@@ -3357,6 +3382,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->OnDelete();
|
||||
$this->FireEvent(EventName::BEFORE_DELETE);
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
@@ -3458,6 +3484,8 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
$this->AfterDelete();
|
||||
$this->FireEvent(EventName::AFTER_DELETE);
|
||||
|
||||
|
||||
$this->m_bIsInDB = false;
|
||||
// Fix for N°926: do NOT reset m_iKey as it can be used to have it for reporting purposes (see the REST service to delete
|
||||
@@ -3643,6 +3671,8 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
$aTransitionDef = $aStateTransitions[$sStimulusCode];
|
||||
|
||||
$this->FireEvent(EventName::BEFORE_APPLY_STIMULUS);
|
||||
|
||||
// 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);
|
||||
@@ -3754,6 +3784,8 @@ abstract class DBObject implements iDisplay
|
||||
/** @var \Trigger $oTrigger */
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
|
||||
$this->FireEvent(EventName::AFTER_APPLY_STIMULUS);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5384,5 +5416,23 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
return $oExpression->Evaluate($aArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sEvent
|
||||
* @param array $aEventData
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function FireEvent($sEvent, $aEventData = array())
|
||||
{
|
||||
$aEventData['debug_info'] = 'from: '.get_class($this).':'.$this->GetKey();
|
||||
$aEventData['object'] = $this;
|
||||
$aEventSources = array($this->m_sEventUniqId);
|
||||
foreach (MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL, false) as $sClass)
|
||||
{
|
||||
$aEventSources[] = $sClass;
|
||||
}
|
||||
Event::FireEvent($sEvent, $aEventSources, $aEventData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -603,16 +603,24 @@ abstract class LogAPI
|
||||
}
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
if (self::CanLog($sLevel, $sChannel))
|
||||
{
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static function CanLog($sLevel, $sChannel = null)
|
||||
{
|
||||
if (! static::$m_oFileLog)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! isset(self::$aLevelsPriority[$sLevel]))
|
||||
{
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_null($sChannel))
|
||||
@@ -621,25 +629,21 @@ abstract class LogAPI
|
||||
}
|
||||
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (is_string($sMinLogLevel))
|
||||
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
||||
{
|
||||
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
||||
{
|
||||
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
}
|
||||
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
||||
{
|
||||
//priority too low regarding the conf, do not log this
|
||||
return;
|
||||
}
|
||||
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
}
|
||||
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
||||
{
|
||||
//priority too low regarding the conf, do not log this
|
||||
return false;
|
||||
}
|
||||
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -830,4 +834,4 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
|
||||
throw new ProcessException(self::class.' : The configured filename builder is invalid (log_filename_builder_impl="'.$sLogFileNameBuilder.'")');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Service\EventName;
|
||||
use Combodo\iTop\Service\Event;
|
||||
|
||||
|
||||
/**
|
||||
* ormDocument
|
||||
@@ -188,7 +191,6 @@ class ormDocument
|
||||
* @param string $sContentDisposition Either 'inline' or 'attachment'
|
||||
* @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file
|
||||
* @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField
|
||||
* @return none
|
||||
*/
|
||||
public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null)
|
||||
{
|
||||
@@ -207,6 +209,12 @@ class ormDocument
|
||||
$oDocument = $oObj->Get($sAttCode);
|
||||
if (is_object($oDocument))
|
||||
{
|
||||
$aEventData = array(
|
||||
'debug_info' => $oDocument->GetFileName(),
|
||||
'object' => $oObj,
|
||||
'document' => $oDocument,
|
||||
);
|
||||
Event::FireEvent(EventName::DOWNLOAD_DOCUMENT, $sClass, $aEventData);
|
||||
$oPage->TrashUnexpectedOutput();
|
||||
$oPage->SetContentType($oDocument->GetMimeType());
|
||||
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
|
||||
@@ -28,6 +28,8 @@ use BinaryExpression;
|
||||
use Combodo\iTop\Portal\Brick\CreateBrick;
|
||||
use Combodo\iTop\Portal\Helper\ApplicationHelper;
|
||||
use Combodo\iTop\Portal\Helper\ContextManipulatorHelper;
|
||||
use Combodo\iTop\Service\Event;
|
||||
use Combodo\iTop\Service\EventName;
|
||||
use DBObject;
|
||||
use DBObjectSearch;
|
||||
use DBObjectSet;
|
||||
@@ -121,6 +123,8 @@ class ObjectController extends BrickController
|
||||
|
||||
$sOperation = $oRequestManipulator->ReadParam('operation', '');
|
||||
|
||||
$oObject->FireEvent(EventName::OBJECT_DETAILS);
|
||||
|
||||
$aData = array('sMode' => 'view');
|
||||
$aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId);
|
||||
$aData['form']['title'] = Dict::Format('Brick:Portal:Object:Form:View:Title', MetaModel::GetName($sObjectClass),
|
||||
|
||||
@@ -150,6 +150,9 @@ return array(
|
||||
'Combodo\\iTop\\Composer\\iTopComposer' => $baseDir . '/sources/Composer/iTopComposer.php',
|
||||
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\Service\\Event' => $baseDir . '/sources/application/Service/Event.php',
|
||||
'Combodo\\iTop\\Service\\EventData' => $baseDir . '/sources/application/Service/EventData.php',
|
||||
'Combodo\\iTop\\Service\\EventName' => $baseDir . '/sources/application/Service/EventName.php',
|
||||
'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php',
|
||||
'Config' => $baseDir . '/core/config.class.inc.php',
|
||||
'ConfigException' => $baseDir . '/core/config.class.inc.php',
|
||||
|
||||
@@ -380,6 +380,9 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
|
||||
'Combodo\\iTop\\Composer\\iTopComposer' => __DIR__ . '/../..' . '/sources/Composer/iTopComposer.php',
|
||||
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
|
||||
'Combodo\\iTop\\Service\\Event' => __DIR__ . '/../..' . '/sources/application/Service/Event.php',
|
||||
'Combodo\\iTop\\Service\\EventData' => __DIR__ . '/../..' . '/sources/application/Service/EventData.php',
|
||||
'Combodo\\iTop\\Service\\EventName' => __DIR__ . '/../..' . '/sources/application/Service/EventName.php',
|
||||
'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php',
|
||||
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
'ConfigException' => __DIR__ . '/../..' . '/core/config.class.inc.php',
|
||||
|
||||
@@ -241,8 +241,9 @@ class MFCompiler
|
||||
$this->Log("Root class (with child classes): ".$oClass->getAttribute('id'));
|
||||
$this->aRootClasses[$oClass->getAttribute('id')] = $oClass;
|
||||
}
|
||||
|
||||
$this->LoadSnippets();
|
||||
|
||||
$this->LoadSnippets();
|
||||
$this->LoadHooks();
|
||||
|
||||
// Compile, module by module
|
||||
//
|
||||
@@ -1119,6 +1120,67 @@ EOF
|
||||
$aClassParams['indexes'] = var_export($aIndexes, true);
|
||||
}
|
||||
|
||||
$sEvents = '';
|
||||
$sMethods = '';
|
||||
$oHooks = $oClass->GetOptionalElement('hooks');
|
||||
if ($oHooks)
|
||||
{
|
||||
foreach($oHooks->getElementsByTagName('hook') as $oHook)
|
||||
{
|
||||
/** @var \DOMElement $oHook */
|
||||
$sEventName = $oHook->getAttribute('id');
|
||||
$oListeners = $oHook->GetOptionalElement('listeners');
|
||||
if ($oListeners)
|
||||
{
|
||||
foreach ($oListeners->getElementsByTagName('listener') as $oListener)
|
||||
{
|
||||
/** @var \DOMElement $oListener */
|
||||
$sListenerId = $oListener->getAttribute('id');
|
||||
$oCallback = $oListener->GetUniqueElement('callback', false);
|
||||
if (is_object($oCallback))
|
||||
{
|
||||
$sCallback = $oCallback->GetText();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExecute = $oListener->GetUniqueElement('execute', true);
|
||||
$sExecute = trim($oExecute->GetText());
|
||||
$sCallback = "EventHook_{$sEventName}_{$sListenerId}";
|
||||
$sCallbackFct = preg_replace('@^function\s*\(@', "public function {$sCallback}(", $sExecute);
|
||||
if ($sExecute == $sCallbackFct)
|
||||
{
|
||||
throw new DOMFormatException("Malformed tag <execute> in class: {$sClass} hook: {$sEventName} listener: {$sListenerId}");
|
||||
}
|
||||
$sMethods .= "\n {$sCallbackFct}\n\n";
|
||||
}
|
||||
if (strpos($sCallback, '::') === false)
|
||||
{
|
||||
$sEventListener = 'array($this, \''.$sCallback.'\')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEventListener = "'{$sCallback}'";
|
||||
}
|
||||
|
||||
$sListenerPriority = (float)($oListener->GetChildText('priority', '0'));
|
||||
$sEvents .= "\n Combodo\iTop\Service\Event::Register(\"$sEventName\", $sEventListener, \$this->m_sEventUniqId, \"$sListenerId\", null, $sListenerPriority);";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($sEvents))
|
||||
{
|
||||
$sMethods .= <<<EOF
|
||||
protected function RegisterEvents()
|
||||
{
|
||||
parent::RegisterEvents();
|
||||
{$sEvents}
|
||||
}
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
if ($oArchive = $oProperties->GetOptionalElement('archive'))
|
||||
{
|
||||
$bEnabled = $this->GetPropBoolean($oArchive, 'enabled', false);
|
||||
@@ -1862,7 +1924,6 @@ EOF
|
||||
}
|
||||
|
||||
// Methods
|
||||
$sMethods = "";
|
||||
$oMethods = $oClass->GetUniqueElement('methods');
|
||||
foreach($oMethods->getElementsByTagName('method') as $oMethod)
|
||||
{
|
||||
@@ -1993,6 +2054,7 @@ $sLifecycle
|
||||
$sHighlightScale
|
||||
$sZlists;
|
||||
EOF;
|
||||
|
||||
// some other stuff (magical attributes like friendlyName) are done in MetaModel::InitClasses and though not present in the
|
||||
// generated PHP
|
||||
$aPHP[$sClassName] = $this->GeneratePhpCodeForClass($sClassName, $sParentClass, $sClassParams, $sInitMethodCalls,
|
||||
@@ -2930,6 +2992,130 @@ EOF;
|
||||
foreach($this->aSnippets as $sModuleId => $void)
|
||||
{
|
||||
uasort($this->aSnippets[$sModuleId]['before'], array(get_class($this), 'SortOnRank'));
|
||||
uasort($this->aSnippets[$sModuleId]['after'], array(get_class($this), 'SortOnRank'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
protected function LoadHooks()
|
||||
{
|
||||
$sClassName = 'GlobalEventHooks';
|
||||
$sModuleId = '_core_';
|
||||
if (!array_key_exists($sModuleId, $this->aSnippets))
|
||||
{
|
||||
$this->aSnippets[$sModuleId] = array('before' => array(), 'after' => array());
|
||||
}
|
||||
$oHooks = $this->oFactory->GetNodes('/itop_design/hooks/hook');
|
||||
$aHooks = array();
|
||||
foreach($oHooks as $oHook)
|
||||
{
|
||||
/** @var \DOMElement $oHook */
|
||||
$sEventName = $oHook->getAttribute('id');
|
||||
$oListeners = $oHook->GetOptionalElement('listeners');
|
||||
if ($oListeners)
|
||||
{
|
||||
foreach ($oListeners->getElementsByTagName('listener') as $oListener)
|
||||
{
|
||||
/** @var \DOMElement $oListener */
|
||||
$sListenerId = $oListener->getAttribute('id');
|
||||
$oExecute = $oListener->GetUniqueElement('execute', true);
|
||||
$sExecute = trim($oExecute->GetText());
|
||||
$sCallback = "{$sEventName}_{$sListenerId}";
|
||||
$sCallbackFct = preg_replace('@^function\s*\(@', "public static function {$sCallback}(", $sExecute);
|
||||
if ($sExecute == $sCallbackFct)
|
||||
{
|
||||
throw new DOMFormatException("Malformed tag <execute> in hook: {$sEventName} listener: {$sListenerId}");
|
||||
}
|
||||
$fPriority = (float)($oListener->GetChildText('priority', '0'));
|
||||
|
||||
$aFilters = array();
|
||||
$oFilters = $oListener->GetOptionalElement('filters');
|
||||
if ($oFilters)
|
||||
{
|
||||
foreach ($oFilters->getElementsByTagName('filter') as $oFilter)
|
||||
{
|
||||
$aFilters[] = $oFilter->GetText();
|
||||
}
|
||||
}
|
||||
if (empty($aFilters))
|
||||
{
|
||||
$sEventSource = 'null';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEventSource = 'array("'.implode('", "', $aFilters).'")';
|
||||
}
|
||||
|
||||
$aContexts = array();
|
||||
$oContexts = $oListener->GetOptionalElement('Contexts');
|
||||
if ($oContexts)
|
||||
{
|
||||
foreach ($oContexts->getElementsByTagName('Context') as $oContext)
|
||||
{
|
||||
$aContexts[] = $oContext->GetText();
|
||||
}
|
||||
}
|
||||
if (empty($aContexts))
|
||||
{
|
||||
$sContext = 'null';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sContext = 'array("'.implode('", "', $aContexts).'")';
|
||||
}
|
||||
|
||||
$aHooks[] = array(
|
||||
'event_name' => $sEventName,
|
||||
'callback' => $sCallback,
|
||||
'content' => $sCallbackFct,
|
||||
'priority' => $fPriority,
|
||||
'source' => $sEventSource,
|
||||
'context' => $sContext,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($aHooks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$sRegister = '';
|
||||
$sMethods = '';
|
||||
foreach ($aHooks as $aHook)
|
||||
{
|
||||
$sCallback = $aHook['callback'];
|
||||
$sEventName = $aHook['event_name'];
|
||||
$sEventSource = $aHook['source'];
|
||||
$sContext = $aHook['context'];
|
||||
$sPriority = $aHook['priority'];
|
||||
$sRegister .= "\nCombodo\iTop\Service\Event::Register(\"$sEventName\", '$sClassName::$sCallback', $sEventSource, null, $sContext, $sPriority);";
|
||||
$sCallbackFct = $aHook['content'];
|
||||
$sMethods .= "\n {$sCallbackFct}\n\n";
|
||||
}
|
||||
|
||||
$sContent = <<<PHP
|
||||
class $sClassName
|
||||
{
|
||||
$sMethods
|
||||
}
|
||||
|
||||
$sRegister
|
||||
|
||||
PHP;
|
||||
|
||||
$fOrder = 0;
|
||||
$this->aSnippets[$sModuleId]['after'][] = array(
|
||||
'rank' => $fOrder,
|
||||
'content' => $sContent,
|
||||
'snippet_id' => $sClassName,
|
||||
);
|
||||
foreach($this->aSnippets as $sModuleId => $void)
|
||||
{
|
||||
uasort($this->aSnippets[$sModuleId]['after'], array(get_class($this), 'SortOnRank'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
298
sources/application/Service/Event.php
Normal file
298
sources/application/Service/Event.php
Normal file
@@ -0,0 +1,298 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Service;
|
||||
|
||||
use Closure;
|
||||
use ContextTag;
|
||||
use Exception;
|
||||
use ExecutionKPI;
|
||||
use IssueLog;
|
||||
|
||||
define('LOG_EVENT_SERVICE_CHANNEL', 'EventService');
|
||||
|
||||
class Event
|
||||
{
|
||||
private static $aEvents = array();
|
||||
private static $iEventIdCounter = 0;
|
||||
|
||||
/**
|
||||
* Register a callback for a specific event
|
||||
*
|
||||
* @param string $sEvent corresponding event
|
||||
* @param callable $callback The callback to call
|
||||
* @param array|string|null $sEventSource event filtering depending on the source of the event
|
||||
* @param array $aCallbackData optional data given by the registrar to the callback
|
||||
* @param array|string|null $context context filter
|
||||
* @param float $fPriority optional priority for callback order
|
||||
*
|
||||
* @return string Id of the registration (used for unregistering)
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function Register($sEvent, callable $callback, $sEventSource = null, $aCallbackData = array(), $context = null, $fPriority = 0.0)
|
||||
{
|
||||
is_callable($callback, false, $sName);
|
||||
|
||||
$aEventCallbacks = isset(self::$aEvents[$sEvent]) ? self::$aEvents[$sEvent] : array();
|
||||
$sId = 'event_'.self::$iEventIdCounter++;
|
||||
$aEventCallbacks[] = array(
|
||||
'id' => $sId,
|
||||
'callback' => $callback,
|
||||
'source' => $sEventSource,
|
||||
'name' => $sName,
|
||||
'data' => $aCallbackData,
|
||||
'context' => $context,
|
||||
'priority' => $fPriority,
|
||||
);
|
||||
usort($aEventCallbacks, function ($a, $b) {
|
||||
$fPriorityA = $a['priority'];
|
||||
$fPriorityB = $b['priority'];
|
||||
if ($fPriorityA == $fPriorityB) {
|
||||
return 0;
|
||||
}
|
||||
return ($fPriorityA < $fPriorityB) ? -1 : 1;
|
||||
});
|
||||
self::$aEvents[$sEvent] = $aEventCallbacks;
|
||||
|
||||
if (IssueLog::CanLog(IssueLog::LEVEL_DEBUG, LOG_EVENT_SERVICE_CHANNEL))
|
||||
{
|
||||
$iTotalRegistrations = 0;
|
||||
foreach (self::$aEvents as $aEvent)
|
||||
{
|
||||
$iTotalRegistrations += count($aEvent);
|
||||
}
|
||||
$sEventName = "$sEvent:".self::GetSourcesAsString($sEventSource);
|
||||
IssueLog::Trace("Registering event '$sEventName' for '$sName' with id '$sId' (total $iTotalRegistrations)", LOG_EVENT_SERVICE_CHANNEL);
|
||||
}
|
||||
return $sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an event. Call all the callbacks registered for this event.
|
||||
*
|
||||
* @param string $sEvent event to trigger
|
||||
* @param string|array $eventSource source of the event
|
||||
* @param array $aEventData event related data
|
||||
*
|
||||
* @throws \Exception from the callback
|
||||
*/
|
||||
public static function FireEvent($sEvent, $eventSource = null, $aEventData = array())
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSource = isset($aEventData['debug_info']) ? " {$aEventData['debug_info']}" : '';
|
||||
$sEventName = "$sEvent:".self::GetSourcesAsString($eventSource);
|
||||
IssueLog::Trace("Fire event '$sEventName'$sSource", LOG_EVENT_SERVICE_CHANNEL);
|
||||
if (!isset(self::$aEvents[$sEvent]))
|
||||
{
|
||||
IssueLog::Trace("No registration found for event '$sEvent'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
$oKPI->ComputeStats('FireEvent', $sEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (self::$aEvents[$sEvent] as $aEventCallback)
|
||||
{
|
||||
if (!self::MatchEventSource($aEventCallback['source'], $eventSource))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!self::MatchContext($aEventCallback['context']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sName = $aEventCallback['name'];
|
||||
IssueLog::Debug("Fire event '$sEventName'$sSource calling '{$sName}'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
try
|
||||
{
|
||||
if (is_callable($aEventCallback['callback']))
|
||||
{
|
||||
call_user_func($aEventCallback['callback'], new EventData($sEvent, $eventSource, $aEventData, $aEventCallback['data']));
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Debug("Callback '{$sName}' not a callable anymore, unregister", LOG_EVENT_SERVICE_CHANNEL);
|
||||
self::UnRegisterCallback($aEventCallback['id']);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error("Event '$sEventName' for '{$sName}' id {$aEventCallback['id']} failed with error: ".$e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$oKPI->ComputeStats('FireEvent', $sEvent);
|
||||
}
|
||||
|
||||
private static function MatchEventSource($srcRegistered, $srcEvent)
|
||||
{
|
||||
if (empty($srcRegistered))
|
||||
{
|
||||
// no filtering
|
||||
return true;
|
||||
}
|
||||
if (empty($srcEvent))
|
||||
{
|
||||
// no match (the registered source is not empty)
|
||||
return false;
|
||||
}
|
||||
if (is_string($srcRegistered))
|
||||
{
|
||||
$aSrcRegistered = array($srcRegistered);
|
||||
}
|
||||
elseif (is_array($srcRegistered))
|
||||
{
|
||||
$aSrcRegistered = $srcRegistered;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSrcRegistered = array();
|
||||
}
|
||||
|
||||
if (is_string($srcEvent))
|
||||
{
|
||||
$aSrcEvent = array($srcEvent);
|
||||
}
|
||||
elseif (is_array($srcEvent))
|
||||
{
|
||||
$aSrcEvent = $srcEvent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aSrcEvent = array();
|
||||
}
|
||||
|
||||
foreach ($aSrcRegistered as $sSrcRegistered)
|
||||
{
|
||||
foreach ($aSrcEvent as $sSrcEvent)
|
||||
{
|
||||
if ($sSrcRegistered == $sSrcEvent)
|
||||
{
|
||||
// sources matches
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// no match
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function MatchContext($registeredContext)
|
||||
{
|
||||
if (empty($registeredContext))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (is_string($registeredContext))
|
||||
{
|
||||
$aContexts = array($registeredContext);
|
||||
}
|
||||
elseif (is_array($registeredContext))
|
||||
{
|
||||
$aContexts = $registeredContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
foreach ($aContexts as $sContext)
|
||||
{
|
||||
if (ContextTag::Check($sContext))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function GetSourcesAsString($srcRegistered)
|
||||
{
|
||||
if (empty($srcRegistered))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
if (is_string($srcRegistered))
|
||||
{
|
||||
return $srcRegistered;
|
||||
}
|
||||
if (is_array($srcRegistered))
|
||||
{
|
||||
$sStr = implode(',', $srcRegistered);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a previously registered callback
|
||||
*
|
||||
* @param string $sId the callback registration id
|
||||
*/
|
||||
public static function UnRegisterCallback($sId)
|
||||
{
|
||||
$bRemoved = self::Browse(function ($sEvent, $idx, $aEventCallback) use ($sId) {
|
||||
if ($aEventCallback['id'] == $sId)
|
||||
{
|
||||
$sName = self::$aEvents[$sEvent][$idx]['name'];
|
||||
unset (self::$aEvents[$sEvent][$idx]);
|
||||
IssueLog::Trace("Unregistered callback '{$sName}' id {$sId}' on event '{$sEvent}'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!$bRemoved)
|
||||
{
|
||||
IssueLog::Trace("No registration found for callback '{$sId}'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an event
|
||||
*
|
||||
* @param string $sEvent event to unregister
|
||||
*/
|
||||
public static function UnRegisterEvent($sEvent)
|
||||
{
|
||||
if (!isset(self::$aEvents[$sEvent]))
|
||||
{
|
||||
IssueLog::Trace("No registration found for event '$sEvent'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
return;
|
||||
}
|
||||
|
||||
unset(self::$aEvents[$sEvent]);
|
||||
IssueLog::Trace("Unregistered all the callbacks on event '{$sEvent}'", LOG_EVENT_SERVICE_CHANNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all the events
|
||||
*/
|
||||
public static function UnRegisterAll()
|
||||
{
|
||||
self::$aEvents = array();
|
||||
IssueLog::Trace("Unregistered all events", LOG_EVENT_SERVICE_CHANNEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Browse all the registrations
|
||||
*
|
||||
* @param \Closure $callback function($sEvent, $idx, $aEventCallback) to call (return false to interrupt the browsing)
|
||||
*
|
||||
* @return bool true if interrupted else false
|
||||
*/
|
||||
private static function Browse(closure $callback)
|
||||
{
|
||||
foreach (self::$aEvents as $sEvent => $aCallbackList)
|
||||
{
|
||||
foreach ($aCallbackList as $idx => $aEventCallback)
|
||||
{
|
||||
if (call_user_func($callback, $sEvent, $idx, $aEventCallback) === false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
84
sources/application/Service/EventData.php
Normal file
84
sources/application/Service/EventData.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Service;
|
||||
|
||||
|
||||
/**
|
||||
* Data given to the Event Service callbacks
|
||||
* Class EventServiceData
|
||||
*
|
||||
* @package Combodo\iTop\Service
|
||||
*/
|
||||
class EventData
|
||||
{
|
||||
private $sEvent;
|
||||
private $sEventSource;
|
||||
private $mEventData;
|
||||
private $mCallbackData;
|
||||
|
||||
/**
|
||||
* EventServiceData constructor.
|
||||
*
|
||||
* @param string $sEvent
|
||||
* @param string $sEventSource
|
||||
* @param array $aEventData
|
||||
* @param array $aCallbackData
|
||||
*/
|
||||
public function __construct($sEvent, $sEventSource, $aEventData, $aCallbackData)
|
||||
{
|
||||
$this->sEvent = $sEvent;
|
||||
$this->mEventData = $aEventData;
|
||||
$this->sEventSource = $sEventSource;
|
||||
$this->mCallbackData = $aCallbackData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetEvent()
|
||||
{
|
||||
return $this->sEvent;
|
||||
}
|
||||
|
||||
public function Get($sParam)
|
||||
{
|
||||
if (is_array($this->mEventData) && isset($this->mEventData[$sParam]))
|
||||
{
|
||||
return $this->mEventData[$sParam];
|
||||
}
|
||||
|
||||
if (is_array($this->mCallbackData) && isset($this->mCallbackData[$sParam]))
|
||||
{
|
||||
return $this->mCallbackData[$sParam];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetEventSource()
|
||||
{
|
||||
return $this->sEventSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetEventData()
|
||||
{
|
||||
return $this->mEventData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetCallbackData()
|
||||
{
|
||||
return $this->mCallbackData;
|
||||
}
|
||||
}
|
||||
38
sources/application/Service/EventName.php
Normal file
38
sources/application/Service/EventName.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
namespace Combodo\iTop\Service;
|
||||
|
||||
|
||||
class EventName
|
||||
{
|
||||
// Core
|
||||
//
|
||||
// OrmDocument
|
||||
const DOWNLOAD_DOCUMENT = 'DownloadDocument';
|
||||
|
||||
// DBObject
|
||||
const DB_OBJECT_LOADED = 'DBObjectLoaded';
|
||||
const DB_OBJECT_RELOAD = 'DBObjectReload';
|
||||
const DB_OBJECT_NEW = 'DBObjectNew';
|
||||
const BEFORE_INSERT = 'BeforeInsert';
|
||||
const DB_OBJECT_KEY_READY = 'DBObjectKeyReady';
|
||||
const AFTER_INSERT = 'AfterInsert';
|
||||
const BEFORE_UPDATE = 'BeforeUpdate';
|
||||
const AFTER_UPDATE = 'AfterUpdate';
|
||||
const BEFORE_DELETE = 'BeforeDelete';
|
||||
const AFTER_DELETE = 'AfterDelete';
|
||||
const BEFORE_APPLY_STIMULUS = 'BeforeApplyStimulus';
|
||||
const AFTER_APPLY_STIMULUS = 'AfterApplyStimulus';
|
||||
const ON_CHECK_TO_WRITE = 'OnCheckToWrite';
|
||||
const ON_CHECK_TO_DELETE = 'OnCheckToDelete';
|
||||
const OBJECT_DETAILS = 'ObjectDetails';
|
||||
|
||||
// Application
|
||||
//
|
||||
const LOGIN = 'OnLogin';
|
||||
}
|
||||
@@ -59,6 +59,9 @@
|
||||
<testsuite name="Status">
|
||||
<directory>status</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Service">
|
||||
<directory>service</directory>
|
||||
</testsuite>
|
||||
<testsuite name="CoreExtensions">
|
||||
<directory>coreExtensions</directory>
|
||||
</testsuite>
|
||||
|
||||
406
test/service/EventTest.php
Normal file
406
test/service/EventTest.php
Normal file
@@ -0,0 +1,406 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Service;
|
||||
|
||||
use Combodo\iTop\Service\Event;
|
||||
use Combodo\iTop\Service\EventData;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use ContextTag;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Class EventTest
|
||||
*
|
||||
* @package Combodo\iTop\Test\UnitTest\Service
|
||||
*
|
||||
* @runTestsInSeparateProcesses
|
||||
* @preserveGlobalState disabled
|
||||
* @backupGlobals disabled
|
||||
*/
|
||||
class EventTest extends ItopTestCase
|
||||
{
|
||||
const USE_TRANSACTION = false;
|
||||
const CREATE_TEST_ORG = false;
|
||||
|
||||
private static $iEventCalls;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
self::$iEventCalls = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider BadCallbackProvider
|
||||
*
|
||||
* @param $callback
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testRegisterBadCallback($callback)
|
||||
{
|
||||
$this->expectException(TypeError::class);
|
||||
Event::Register('event', $callback);
|
||||
}
|
||||
|
||||
public function BadCallbackProvider()
|
||||
{
|
||||
return array(
|
||||
array('toto'),
|
||||
array('EventTest::toto'),
|
||||
array(array('EventTest', 'toto')),
|
||||
array(array($this, 'toto')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testNoParameterCallbackFunction()
|
||||
{
|
||||
$sId = Event::Register('event', function () { $this->debug("Closure: event received !!!"); self::IncrementCallCount(); });
|
||||
$this->debug("Registered $sId");
|
||||
Event::FireEvent('event');
|
||||
$this->assertEquals(1, self::$iEventCalls);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider GoodCallbackProvider
|
||||
*
|
||||
* @param $callback
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testMethodCallbackFunction(callable $callback)
|
||||
{
|
||||
$sId = Event::Register('event', $callback);
|
||||
$this->debug("Registered $sId");
|
||||
Event::FireEvent('event');
|
||||
$this->assertEquals(1, self::$iEventCalls);
|
||||
Event::FireEvent('event');
|
||||
$this->assertEquals(2, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function GoodCallbackProvider()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
return array(
|
||||
'method' => array(array($oReceiver, 'OnEvent1')),
|
||||
'static' => array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent1'),
|
||||
'static2' => array(array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver', 'OnStaticEvent1')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testBrokenCallback()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event_a', array($oReceiver, 'BrokenCallback'));
|
||||
|
||||
$this->expectException(TypeError::class);
|
||||
Event::FireEvent('event_a');
|
||||
}
|
||||
|
||||
public function testRemovedCallback()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event_a', array($oReceiver, 'OnEvent1'));
|
||||
|
||||
$oReceiver = null;
|
||||
gc_collect_cycles();
|
||||
|
||||
Event::FireEvent('event_a');
|
||||
$this->assertEquals(1, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testMultiEvent()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event_a', array($oReceiver, 'OnEvent1'));
|
||||
Event::Register('event_a', array($oReceiver, 'OnEvent2'));
|
||||
Event::Register('event_a', array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver', 'OnStaticEvent1'));
|
||||
Event::Register('event_a', 'Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent2');
|
||||
|
||||
Event::Register('event_b', array($oReceiver, 'OnEvent1'));
|
||||
Event::Register('event_b', array($oReceiver, 'OnEvent2'));
|
||||
Event::Register('event_b', array('Combodo\iTop\Test\UnitTest\Service\TestEventReceiver', 'OnStaticEvent1'));
|
||||
Event::Register('event_b', 'Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent2');
|
||||
|
||||
Event::FireEvent('event_a');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
Event::FireEvent('event_b');
|
||||
$this->assertEquals(8, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testMultiSameEvent()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testData()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event1', array($oReceiver, 'OnEventWithData'), '');
|
||||
Event::Register('event1', array($oReceiver, 'OnEventWithData'), '');
|
||||
Event::FireEvent('event1', '', array('text' => 'Event Data 1'));
|
||||
$this->assertEquals(2, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testPriority()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent1'), '', null, null, 0);
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent2'), '', null, null, 1);
|
||||
|
||||
Event::Register('event2', array($oReceiver, 'OnEvent1'), '', null, null, 1);
|
||||
Event::Register('event2', array($oReceiver, 'OnEvent2'), '', null, null, 0);
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(2, self::$iEventCalls);
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testContext()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent1'), '', null, null, 0);
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent2'), '', null, 'test_context', 1);
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(1, self::$iEventCalls);
|
||||
ContextTag::AddContext('test_context');
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(3, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testEventSource()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent1'), 'A', null, null, 0);
|
||||
Event::Register('event1', array($oReceiver, 'OnEvent2'), 'A', null, null, 1);
|
||||
Event::Register('event1', 'Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent1', null, null, null, 2);
|
||||
|
||||
Event::Register('event2', array($oReceiver, 'OnEvent1'), 'A', null, null, 1);
|
||||
Event::Register('event2', 'Combodo\iTop\Test\UnitTest\Service\TestEventReceiver::OnStaticEvent1', null, null, null, 2);
|
||||
Event::Register('event2', array($oReceiver, 'OnEvent2'), 'B', null, null, 0);
|
||||
|
||||
Event::FireEvent('event1', 'A');
|
||||
$this->assertEquals(3, self::$iEventCalls);
|
||||
Event::FireEvent('event2', 'A');
|
||||
$this->assertEquals(5, self::$iEventCalls);
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(6, self::$iEventCalls);
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(7, self::$iEventCalls);
|
||||
Event::FireEvent('event2', array('A', 'B'));
|
||||
$this->assertEquals(10, self::$iEventCalls);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testUnRegisterEvent()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event2', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(3, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
|
||||
Event::UnRegisterEvent('event1');
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(5, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testUnRegisterAll()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event2', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(3, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
|
||||
Event::UnRegisterAll();
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public function testUnRegisterCallback()
|
||||
{
|
||||
$oReceiver = new TestEventReceiver();
|
||||
$sIdToRemove = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sIdToRemove");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event1', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
$sId = Event::Register('event2', array($oReceiver, 'OnEvent1'));
|
||||
$this->debug("Registered $sId");
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(3, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(4, self::$iEventCalls);
|
||||
|
||||
Event::UnRegisterCallback($sIdToRemove);
|
||||
|
||||
Event::FireEvent('event1');
|
||||
$this->assertEquals(6, self::$iEventCalls);
|
||||
|
||||
Event::FireEvent('event2');
|
||||
$this->assertEquals(7, self::$iEventCalls);
|
||||
}
|
||||
|
||||
public static function IncrementCallCount()
|
||||
{
|
||||
self::$iEventCalls++;
|
||||
}
|
||||
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public static function DebugStatic($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestEventReceiver
|
||||
{
|
||||
|
||||
// Event callbacks
|
||||
public function OnEvent1(EventData $oData)
|
||||
{
|
||||
$sEvent = $oData->GetEvent();
|
||||
$this->Debug(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
// Event callbacks
|
||||
public function BrokenCallback(array $aData)
|
||||
{
|
||||
$sEvent = $aData['event'];
|
||||
$this->Debug(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
// Event callbacks
|
||||
public function OnEvent2(EventData $oData)
|
||||
{
|
||||
$sEvent = $oData->GetEvent();
|
||||
$this->Debug(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
public function OnEventWithData(EventData $oData)
|
||||
{
|
||||
$sEvent = $oData->GetEvent();
|
||||
$mEventData = $oData->GetEventData();
|
||||
$this->Debug(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
$this->Debug($mEventData);
|
||||
}
|
||||
|
||||
// Event callbacks
|
||||
public static function OnStaticEvent1(EventData $oData)
|
||||
{
|
||||
$sEvent = $oData->GetEvent();
|
||||
self::DebugStatic(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
// Event callbacks
|
||||
public static function OnStaticEvent2(EventData $oData)
|
||||
{
|
||||
$sEvent = $oData->GetEvent();
|
||||
self::DebugStatic(__METHOD__.": received event '{$sEvent}'");
|
||||
EventTest::IncrementCallCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public static function DebugStatic($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* static version of the debug to be accessible from other objects
|
||||
*
|
||||
* @param $sMsg
|
||||
*/
|
||||
public function Debug($sMsg)
|
||||
{
|
||||
if (DEBUG_UNIT_TEST)
|
||||
{
|
||||
if (is_string($sMsg))
|
||||
{
|
||||
echo "$sMsg\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r($sMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user