For Practical Code Examples See: Sample Code

DokuWiki Event Handlers

DokuWiki users can create and trigger their own events and they can register to be notified of events triggered by DokuWiki. This documentation looks at how to use and write handlers for the events which are triggered by Dokuwiki and are described in the DokuWiki events_list. What follows is based on my own experience with plugins and event handlers and is at least in part meant to clarify things for myself as well as to help others to implement these extremely useful features.

Myron Turner 2007/11/03 12:19

The Event Loop

To begin with, it's helpful to understand how events are processed. To be notified of an event, your code must register to receive the notification. Registering places your request for notification on a list of requests for the event. The list is a first come, first serve array; that is, it's treated as a stack where each next request is pushed on the end of the list. This means it's possible to implement more than one handler for the same event.

When DokuWiki processes an action that has an associated event, it does not perform this action immediately. Instead, it triggers an event for this action. For instance, when DokuWiki goes to write a wiki page, it triggers an IO_WIKIPAGE_WRITE event, which initiates the event notification process.

There are actually two lists: one for before and one for after DokuWiki performs its own action. These are designated by the 'BEFORE' and 'AFTER' keywords, used when registering for an event. Before processing its own action, DokuWiki loops through all the “BEFORE' requests, making its own action the last to be processed. This gives your code the opportunity to act on this event before DokuWiki gets to it. In the case of IO_WIKIPAGE_WRITE, for instance, you can make changes to the page content before it gets sent to the browser.

After performing its own action, DokuWiki loops through all the 'AFTER' requests. In the case of the TPL_ACT_RENDER event, for instance, it's possible to append content to the wiki page. 1)

DokuWiki's action is the default. But during the 'BEFORE' phase of some events, it's also possible to stop the default from taking place. Similarly, an event handler can short-circuit the event loop and prevent any handlers remaining on the stack from being executed.

In simplified pseudo code, the event process would look like this:

     
     var $process_event = true; 
     var $default_stopped = false;

     loop_through_BEFORE_List() {
         return if $process_event == false;
     }

     do_DokuWiki_Action() {
         return if $default_stopped;
     }
     
     var $process_event = true; 

     loop_through_AFTER_List(){
      return if $process_event == false;
     }

Registering an Event

Event handlers are registered using the register_hook() function of the EVENT_HANDLER class, the specifications for which are found on Dokuwiki's events page. The function definition for register_hook() is as follows:

void register_hook(string $event, string $advise,
 mixed &$obj, string $method, mixed $param=NULL)
  • $event is the name of the event, for instance IO_WIKIPAGE_WRITE;
  • $advise is either BEFORE or AFTER;
  • $obj is either a reference, which in a plugin would be the $this variable, or else it is NULL, in which case the callback function must be in the global scope;
  • $method is the name of the callback function which will handle the event;
  • $param is an optional container for any data which the callback might require.

Global Scope

To call register_hook() from the global scope, you set $obj to NULL and use the global variable $EVENT_HANDLER:

$EVENT_HANDLER->register_hook( ... )

$EVENT_HANDLER is a reference to Doku_Event_Handler. It controls the execution of all events, both user-defined and DokuWiki-defined.

Action plugins

Action plugins do not need direct access to the global $EVENT_HANDLER. They are specifically designed handle DokuWiki events, and typically you would be calling register_hook from an action plugin.

In action plugins, event handlers are registered in the register() method, which all action plugins must implement. It takes one parameter, $controller, which is in effect an alias for $EVENT_HANDLER and which is used to register event handlers:

function register(&$controller) {
  $controller->register_hook('TPL_ACT_RENDER','AFTER',$this,'tpl_render');    
}
  • The event is 'TPL_ACT_RENDER', which is activated in inc/template.php by the function which dispatches the page to be formatted and printed.
  • The event handler is tpl_render(), which will be found in the plugin.
  • The $this parameter will give DokuWiki's event module access to the handler.
  • Finally, the handler is to be called after DokuWiki formats the page content.

The Event Handler

The handler has this basic form:

   /**
    * $event: Doku_Event object
    * $param: optional mixed variable
   */ 
function handler (&$event, $param) {
 // handler code
}

When Doku_Event_Handler calls the handler function, it passes in two parameters, the current $event object and $param, which is designed to hold any additional data relevant to the event. $param is the $param that is passed through as a parameter in register_hook(). 2)

The Event Object

The authoritative specification for the event object (Doku_Event) is found on the DokuWiki site and should be consulted.

The $event object has six fields:

  • name: Event Name;
  • data: Specific to each event, data may include, page content, headers, meta data, objects, the current action (e.g. edit, index), whatever is needed of for the execution of the event; it may also hold no data, and it has no fixed structure: it may be a string, array, multidimensional array, obect.
  • result: First it holds the return value from the default handler, which can then be consulted by an 'AFTER' handler. It can then be modified by an 'AFTER' handler, in which case it might be information for any subsequent 'AFTER' handlers. Utlimately it will be returned to the trigger_event() function. In most case this value is either TRUE, if the default action has taken place, or NULL if it hasn't.
  • canPreventDefault: TRUE or FALSE, indicating whether or not the default action for this event can be stopped; this information is available from DokuWiki's events list.
  • _default: TRUE or FALSE, indicating whether the default action for this event should be enabled; its default value is TRUE but can be set to false by calling $event→preventDefault().
  • _continue: This value defaults to TRUE. If $event→stopPropagation() is called, it is set to false, stopping “any further processing of the event by event handlers” but “this function does not prevent the default action taking place”.3)

Methods

  1. $event→preventDefault()
    • Calling this method from a 'BEFORE' handler prevents the default action taking from place; it has no effect if called from an 'AFTER' handler. It sets $event→_default to FALSE. Being able to prevent the default action has considerable utility. It can stop a page from being cached when used with the PARSER_CACHE_USE event. It can also prevent a page from being sent to the browser at various stages of the rendering and output process, each of which is represented by an event. One interesting possibility, which I haven't tested, is preventing the TPL_ACT_UNKNOWN default; this should enable you to set up your own action in response to an unrecognized action request sent from the browser. See the events_list on the DokuWiki site for details and possibilities.
  2. $event→stopPropagation().
    • Calling this method sets $event→_continue to FALSE and stops any further processing of the event by event handlers; it does not prevent the default action taking place. It comes into play where you have set more than one handler for the same event. If you have registered an event for both 'BEFORE' and 'AFTER' execution, canceling the 'BEFORE' does not stop propagation of the 'AFTER' events. (See the above section on the Event Loop ).
  3. mixed trigger_event ($name, &$data, $action=NULL, $canPreventDefault=true)
    • This is a global function, not a method of the Doku_Event class. Calling this function enables the triggering of a user-defined event. Its parameters are as follows:
      • $name: This is the name of the event. If you implement your own event, then this is the name of that event.
      • $data: Whatever is required for your data
      • $action: This is the default action, most probably a function in the global scope.
      • $canPreventDefault: TRUE or FALSE, to indicate whether or not the default action can be stopped. In user-defined events, this cn be left at the default value of TRUE.
      • mixed return value: This is whatever is stored in the event's result property.
    • It's possible to use this function to trigger a standard DokuWiki event, but this would require careful coding and a knowledge of when and how the DokuWiki event is processed. Moreover, triggering a standard event doesn't prevent it from being triggered by DokuWiki. It would also have to be an event which doesn't have a default action,4)since using this function replaces the event's default action with the $action parameter of trigger_event().
1)
Similarly, using 'BEFORE', you can prepend content.
2)
DokuWiki's own events don't seem to use $param , but it is available if needed in user-designed events.
3)
Comment from inc/events/php
4)
For an example see DokuWiki's events page.

User Tools