Events API
Overview
This service provides extensibility through events.
The main behavior is that events providers
register
events they want to fire and events listeners
register
the callback functions to be called when an event is fired.
Publisher
Registration
The providers must register the events in XML prior to firing them in PHP. Registration requires:
-
an event: roughly a unique name
-
event data: parameters provided with the event
-
sources: filtering criteria usable by the listener, optional and event specific
events
The event id should follow the constants guidelines (i.e. uppercase with '_' underscore characters only), and it is also recommended that the event's name contains 'EVENT'.
-
of course it must be unique within iTop and all extensions
-
In the below example, we have named our event EVENT_DOWNLOAD_DOCUMENT
-
A string constant will automatically be created with the same name as the string
const EVENT_DOWNLOAD_DOCUMENT = 'EVENT_DOWNLOAD_DOCUMENT';
event_data
Events data are given to all the listeners by the event provider when the event is fired. It's the information provided with the event, so the listener can act with sufficient information.
-
The Data are specific to the providers and consist in a flat list of values.
-
Nevertheless, the values can be any PHP value like objects or arrays, which must be specified in the declaration
-
The event data and their type are declared in the provider event registration
-
They are specifics to each event
In the below example, 5 event_datum have been declared
-
object
which is a DBObject -
document
which is an ormDocument -
att_code
andcontent_disposition
which are string -
debug_info
which is a string and is always present in Combodo's created events
sources
Events Sources is a way to filter events depending on the source of the event.
For example, for events on datamodel objects, the class of the object is a source that can be used by the listeners. In fact all the parent classes are added to the event sources, so listeners can filter events at any level of the class tree. A listener can specify to listen on “Ticket” events. All the events fired on objects inheriting from Ticket will be sent to this listener.
Result in XML
- itop_design 3.1
-
<events> <event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define"> <description>A document has been downloaded from the GUI</description> <sources> <source id="Document">Document</source> </sources> <event_data> <event_datum id="object"> <description>The object containing the document</description> <type>DBObject</type> </event_datum> <event_datum id="document"> <description>The document downloaded</description> <type>ormDocument</type> </event_datum> <event_datum id="att_code"> <description>Attribute code of the object, containing the document</description> <type>string</type> <event_datum id="content_disposition"> <description>To differentiate if the document was displayed in preview or truly downloaded</description> <type>string</type> <event_datum id="debug_info"> <description>Debug string</description> <type>string</type> </event_datum> </event_data> </event>
All iTop events
-
All iTop core events declaration can be found in <itop>/code/datamodel.application.xml
-
Of course it does not include events which would be brought by an iTop extension
Event firing
This part is written in PHP. It is done by the event provider, when it makes sense to fire the event. It must provides
-
the Event data, compliant with the
id
andtype
declared in the registration -
the applicable Event sources
- ormdocument.class.inc.php
-
public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null) { // ... $aEventData = array( // Array keys are the ids of the XML event_datum 'debug_info' => $oDocument->GetFileName(), // debug_info was declared of type string in registration 'object' => $oObj, // object was declared as a DBObject 'document' => $oDocument, // document was declared as an ormDocument 'att_code' => $sAttCode, 'content_disposition' => $sContentDisposition, ); $aEventSources = []; // We put all parents classes of the current object, so the listeners at any levels are informed foreach (MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL, false) as $sClass) { $aEventSources[] = $sClass; } Combodo\iTop\Service\Events\EventService::FireEvent( // The event name is also a constant, so can be written without quote new Combodo\iTop\Service\Events\EventData(EVENT_DOWNLOAD_DOCUMENT, $aEventSources , $aEventData) ); // ...
Listener
The listener will specify that, if a particular event is trigger on a source which matches the filter he has specified, then a particular PHP code must be executed.
-
event: is the string name of the event
-
callback: the code to execute when an event matching the filter occurs.
-
filter: a set of strings. If the event has one of the filter strings in its
$aEventSources
, then the callback code is executed. For many triggers, those strings correspond to classes names. It's optional. -
rank: If you are not the only one to react to this event, you can specify a rank, to ensure that you will be executed before or after another extension which rank you know
This registration can be done in many different ways
in XML on the class
You can define a listener inside a class User request, for this you specify:
-
event_listener id: give it any name you like, here it must be unique with the class
-
event: under XML tag
event
EVENT_DB_AFTER_WRITE -
callback: under XML tag
callback
provide the name of a method in this class -
rank: under XML tag
rank
-
filter: under XML collection
filters/filter
, but implicit when declared within a class
- itop_design / classes
-
<class id="UserRequest" _created_in="itop-request-mgmt" _delta="must_exist"> <event_listeners> <event_listener id="OnUserRequestUpdateDone" _delta="define"> <event>EVENT_DB_AFTER_WRITE</event> <callback>OnUserRequestUpdateDone</callback> <rank>0</rank> </event_listener> </event_listeners>
Registering to event EVENT_DB_AFTER_WRITE
-
Is similar to overwriting UserRequest::OnUpdate, but without the risk to be overwritten by another customization, as all listeners to an event are called in sequence to do their job
-
Is similar to implementing iObjectApplicationExtension OnDBUpdate(), but for many events, the interface equivalent method does not exist, and with this, you have no control on execution order
in XML with direct code
Here the XML declaration is done outside of any class, at the root of itop_design XML tree.
Note in that case that:
-
the XML filter tag to declare the applicable class(es)
-
If the
event
is not applicable to the class in thefilter
, it will not fail, displays no warning, just the callback will never be called. -
the method has no
name
-
you have no access to
$this
, the current$oObject
is provided by the Event ($oEventData→Get('object'))
- itop_design
-
<event_listeners> <event_listener id="OnAttachmentDownloadActivateTriggers" _delta="define"> <event>EVENT_DOWNLOAD_DOCUMENT</event> <filters> <filter>Attachment</filter> </filters> <rank>0</rank> <code><![CDATA[ function(Combodo\iTop\Service\Events\EventData $oEventData) { // callback code: same content as the OnAttachmentDownloadActivateTriggers function described in the "In PHP" section below if ($oEventData->Get('content_disposition') !== ormDocument::ENUM_CONTENT_DISPOSITION_ATTACHMENT) { return; } $oObject = $oEventData->Get('object'); $sAttCode = $oEventData->Get('att_code'); $oDocument = $oEventData->Get('document'); ... } ]]></ code> <!-- remove the blank between </ and code (wiki limitation) --> </event_listener> </event_listeners>
Callback
If you have registered your listener in XML in a class, then create the callback as a PHP method on that class.
- class:UserRequest
-
public function OnUserRequestUpdateDone(Combodo\iTop\Service\Events\EventData $oEventData) { $sEvent = $oEventData->GetEvent(); // Returns the Event string, in case you would need it $aChange = $oEventData->Get('changes'); // Same as ListChanges() // Do you own treatment }
Within the callback code
-
You can get the event_data of the event publisher registration with
$oEventData->Get('xx') // where 'xx' is the 'id' of the 'event_datum' xml tag in event registration
-
See examples just above
-
In some event, standard methods can be available on the object, such as DBObject::AddCheckIssue(), DBObject::ListChanges(),…
-
When the event and the callback method is declared on the class itself, you can use
$this
instead of
$oObject = $oEventData->Get('object');
Example: on Person change
In standard iTop, we prevent a Portal User to loose his access if someone change his Person's organization, by this event mechanism.
Listener in PHP
If you do this registration and callback in PHP, the easiest is
to declare a new class implementing iEventServiceSetup
In one place you will do the registration in
RegisterEventsAndListeners() and specify the callback
code.
Listeners can specify the sources they want to listen.
-
In the first example, there is any third parameter to specify the sources, so no filter, so any
Document
. -
In the second example we want to handle the specific case of a single type of source the
Attachment
As specified in the top of the page, the event source includes the children of the . e.g. if you place a listener (for example on EVENT_DB_CHECK_TO_WRITE and/or EVENT_DB_LINKS_CHANGED) and set $sEventSource with value User::class, it will be fired during modifications on UserLocal objects.
- sources/Core/EventListener/AttributeBlobEventListener.php
-
class AttributeBlobEventListener implements iEventServiceSetup { public function RegisterEventsAndListeners() { EventService::RegisterListener( EVENT_DOWNLOAD_DOCUMENT, [$this, 'OnAttributeBlobDownloadActivateTrigger'] ); } public function OnAttributeBlobDownloadActivateTrigger(Combodo\iTop\Service\Events\EventData $oEventData): void { // callback code ...
- datamodels/2.x/itop-attachments/src/Hook/EventListener.php
-
class EventListener implements iEventServiceSetup { public function RegisterEventsAndListeners() { EventService::RegisterListener( EVENT_DOWNLOAD_DOCUMENT, [$this, 'OnAttachmentDownloadActivateTriggers'], 'Attachment' ); } public function OnAttachmentDownloadActivateTriggers(Combodo\iTop\Service\Events\EventData $oEventData): void { // callback code if ($oEventData->Get('content_disposition') !== ormDocument::ENUM_CONTENT_DISPOSITION_ATTACHMENT) { return; } $oObject = $oEventData->Get('object'); $sAttCode = $oEventData->Get('att_code'); $oDocument = $oEventData->Get('document'); ...
Existing events
To get more details on the CRUD events
-
Their name, usage
-
Their data
-
DBObject callable methods from callback code