Hooks

Hooks allows you to customise part of ERW's user-interface and database processing, or even to insert your own code in the middle of ERW's. They can be used to add your own custom buttons at the bottom of a list or to check that complex constraints on the content of the database fields are satisfied. Hooks are set up by setting suitable variables in the customisation file.

Writing hooks require some understanding of ERW's internals; you may also need to have a look at the Chapter called Useful Functions. A hook runs in a controlled environment, with a separate variable namespace from the rest of ERW. For your convenience, all relevant global ERW configuration variables are accessible; moreover, for each variable a local copy without the _ERW_ prefix is defined.

Some hooks may pass you arguments: they will be contained in the associative array $arg (the keys and the meaning of the values will vary from hook to hook).

Caution

The local copies are really copies: if you modify them, the corresponding global ERW variable will not be modified. In some cases, this is not what you want.

Besides the global configuration variables, the following ones are available:

$_ERW_db

A PEAR database connection. You can use all PEAR standard methods on it, or you can directly use ERW's internal versions. Note that usually accessing types not owned by the current entity could give rise to a database error.

$_ERW_userId

The id of type usr corresponding to the current user, or 0 if no user has been authenticated, or if the authenticated user is not in usr.

$_ERW_auth

An associative array with keys select, update, insert and delete that describe the current permissions.

$_ERW_locale

The locale that has been negotiated with the browser.

$_ERW_lang

The language part (the first two letters) of the locale that has been negotiated with the browser.

Note again that you can access local copies of these variables under the names $db, $userId, and so on.

The Lock Hook

The lock hook is used during database update and deletion phases. It is set by assigning to the variable $D[type]["lock"] an array containing the names of tables that must be locked together with the neighbourhood of the current entity. It allows you to set up things so that you can atomically check and possibly update your own support tables during an atomic ERW transaction.

The Pre-update Hook

The pre-update hook is called before actually updating the content of a row (or inserting a new row). It is set by assigning to the variable $D[type]["preUpdate"] the name of a PHP file. Note that the file is relative to the server directory where PHP scripts are installed. If it exists, the file is included and its return value (that you can set using the return instruction) is checked. If it is the empty string (or null), the check is considered successful. Otherwise, the returned string is considered to be an error message: the changes will not be applied, the string will be displayed and the user will be presented again with the form he or she was trying to submit. Note that in the case there are supertypes, pre-updates hooks are called starting from the maximal supertypes (i.e., supertypes that have no proper supertype) and going down to the actual type. Pre-update hooks for embedded (entity, relationship and fileset) types are executed afterwards.

To help in writing parametric scripts, the run-time envinronment sets up four global variables:

$mainType

The (entity or relationship) type of the element modified or created by the form currently processed.

$mainId

The numerical identifier (primary key) of the element that caused the current hook invocation, or 0 in case the element is new.

$type

The (entity, relationship or fileset) type of the element modified or created by the hook currently processed. This might differ from $mainType because of embedded editing of relationships, weak entities or filesets.

$id

The numerical identifier (primary key) of the element that caused the current hook invocation.

During the execution of the pre-update hook, you can access a read-only global variable $q (by declaring it as such or using the $GLOBALS array) that contains the query that it is going to be written. It is an associative array with two indices: the first index is a entity type identifier, and the second one an attribute identifier (note that in the case of subtypes, more than one entity type could be involved). The corresponding value is the quoted value that it is going to be written. By this we mean that strings are actually quoted and escaped, and should be passed through the ERW::unquote() function before being examined. Note that in almost all non-mandatory fields one of the possible values is NULL.

During the execution of the pre-update hook, all types in relation with or owned by the current entity type are locked. Queries outside this set could give rise to an error. However, you can use the lock hook to lock additional tables.

Caution

You should not try to change the locks in the pre-update hook. To maintain integrity, it is necessary that the pre-update and the update phase are all executed under the same lock.

Example 1. Checking that a title is long enough

In our library, no book can have a title with less than two characters. Thus, we set in the custom file of the book entity set the line


$D["book"]["preUpdate"] = "bookPreUpdate.php";
and fill the file bookPreUpdate.php with

<?php
global $q;
if (strlen(ERW::unquote($q["book"]["title"])) < 2) return "Title too short!";
else return "";
?>

The Post-update Hook

The post-update hook is called after actually updating the content of a row (or inserting a new row). It is set by assigning to the variable $D[type]["postUpdate"] the name of a PHP file. Note that the file is relative to the server directory where PHP scripts are installed. If it exists, the file is included (you have at your disposal the same variables as in the pre-update hook). Usually at this point some additional update or check of the database is performed.

Post-updates hooks are run initially for embedded (entity, relationship and fileset) types. Then, the hooks for the element modified or created by the form are executed; in the case of supertypes, hooks are invoked from the current type upwards (i.e., exactly in the dual way w.r.t. pre-update hooks). This allows you to impose complex contraints to relationships (even belonging to different types), as you can check them looking at the current database content during the post-update hook.

After your custom checks and changes, you can return null to specify that you are satisfied with the current situation, or a string; in the latter case, the transaction will be aborted and the string will be displayed to the user as an error message (much like a pre-update hook).

Warning

Transaction support is not currently available using MySQL. Returning a non-null value will lead to unpredictable results.

During the execution of the post-update hook, the database is still locked. This can be changed, however, if necessary. The programmer must take into consideration that changing a lock implies that another transaction could sneak in (maybe deleting the row just inserted!). If the lock is not modified, the post-update hook is executed atomically together with the pre-update hook and ERW's update phase.

The Deletion Hooks

Hooks exists also to track entity deletions. They are activated, similarly to the pre-update hook and to the post-update hook, by assigning the variables $D[type]["preDelete"] and $D[type]["postDelete"]. The behaviour is in all ways analogous to the update hooks, except that the only information available is the global variable $id containing the primary key of the entity that it is going to be deleted.

The Post-Authorisation Hook

The post-authorisation hook is called just after the authorisations for the current element or type have been computed. It is set by assigning to the variable $D[type]["postAuth"] the name of a PHP file. Note that the file is relative to the server directory where PHP scripts are installed.

When the hook is run, the associative array $arg will be loaded with three keys: type, providing the type for which the authorisation is being computed, id, providing the element of the given type for which the authorisation is being computed (it will be 0 if the authorisation is not element-specific), and auth, providing the current authorisation array computed by ERW. The latter associates a boolean values to the keys select, update, insert and delete; each value specifies whether the corresponding privilege is granted for the current element or type. Moreover, it associates a string to the keys selectDenied, updateDenied, insertDenied and deleteDenied: the string is the error message produced when the corresponding privilege is denied. Note that in the case of a denied update privilege in the presence of select privileges, the message will be displayed but the user will be anyway presented with a read-only form. Moreover, an empty string will cause no error message to be displayed. You can modify the defaults for these messages by modifying a suitable configuration variable.

During the execution of the post-authorisation hook, you can just assume that the given type and all authorisation-related tables are locked. Queries outside this set could give rise to an error.

Example 2. Old book entries cannot be modified

In our library, books older than 1950 have read-only entries: their data cannot be modified, and they cannot be lent. To obtain this effect, we can just check the publication year in a post-authorisation hook and clear the update privileges in a file named bookPostAuth.php:


<?php
if (ERW::getOne("year from book where id=".$arg["id"]) < 1950) {
   $arg["auth"]["update"] = false;
   $arg["auth"]["updateDenied"] = "You can just browse this entry!";
}
?>
Of course, the custom file for book will contain a line

$D["book"]["postAuth"] = "bookPostAuth.php";

Note that you can also access the usual variables available in a hook: if the restriction does not apply to the users of group 1, then you can modify the hook as follows:


<?php
if ($userGrp != 1 && ERW::getOne("year from book where id=".$arg["id"]) < 1950)
   $arg["auth"]["update"] = false;
?>

The Selection-load Hook

The selection-load hook allows to restrict the elements that will be displayed to the user in selection lists. You can specify for each relation adjacent to the current entity a set of additional tables to insert into the from part of the SQL clause that will be used to fill the list, and also a set of constraints that will be joined to the where part. For instance,


$D["person"]["loadSel"]["loan_book"]["from"] = array("loan");
$D["person"]["loadSel"]["loan_book"]["where"] = 
    array("loan.id1_book=book.id", "loan.enddate is not null");
would restrict the books that can be borrowed to those that are not currently on loan.

Caution

This hook is deprecated, and may not be supported in future versions of ERW

The Button Hook

ERW provides editing for entities and relationships, but there are other services that you might like to provide. For instance, a printed version of an invoice, or a pretty-printed version of the list of entities. The button hook allows you to add buttons at the bottom of lists that invoke JavaScript actions, which could in turn open new windows that would be filled by PHP code.

The button hook is activated by defining in the custom file of an entity (or of a fileset) an associative array named $D[type]["button"]. The first index is the kind of list you want to attach the button to. Possible value are main, meaning that you will add buttons to the main list; sel, meaning that you will add buttons in all lists used to select entities to relate to the current entity; rel, denoting a list of relationships; and fset, denoting the list of files in a fileset. The value of the array for each index must be again an array where each element describes a button. Each element is in turn an associative array with keys label, name, help and events. The values of label and name will be the name and the label of the button, whereas the value associated to the events key is an associative array, having as keys event attributes (onclick, onmousedown etc.) and as values fragments of JavaScript code to be executed. The value of the help element will be displayed as a tooltip when the mouse passes over the button.

Each relevant HTML element in a page generated by ERW has a well-defined identifier. The main list for the entity A is named m_A. There are a number of useful functions that let you access the JavaScript state.

Once you have the data you need, the best thing to do is usually to open a new window and fill it with a suitable PHP script. To this purpose, the function openServiceWindow() is available: it opens a windows without navigation buttons and loads it with the URL given as first parameter; the second parameter is a symbolic name for the window that is purely informative.

Example 3. Pretty-printing a book data.

Our librarian now asks for the possibility of pretty-printing the data of a book, and of printing a report of all lends for a certain book. It is easy to prepare a couple of PHP scripts doing that and outputting HTML. By adding the following to the customisation file for the entity book these features will also be easily accessible from the main list:


$D["book"]["button"]["list"] = array(
  array(
    "label" => 
      "Print data",
    "name" => 
      "data",
    "help" => 
      "Print all data related to the selected book",
    "events" => 
      array(
        "onclick" => 
          "if ((i = getSelectedId('m_book')) < 0) 
             alert('No book selected'); 
           else openServiceWindow('data.php?id='+i, 'data')"
    )
  ),
  array(
      "label" => 
        "Print history",
      "name" => 
        "history",
    "help" => 
      "Print all loans related to the selected book",
    "events" => 
        array(
          "onclick" => 
            "if ((i = getSelectedId('m_book')) < 0) 
               alert('No book selected'); 
             else openServiceWindow('history.php?id='+i, 'history')"
    )
  )
);

Of course, the scripts data.php and history.php must have a look at the variable $id and produce the right data.

The Button Inhibition Hook

Sometimes you may want to keep the user from doing certain things, such as creating new elements, maybe providing your own buttons to do some custom processing (using the button hook). ERW provides a button inhibition hook that allows you to select which of the standard buttons will be displayed. The inhibition hook is activated by defining in the custom file of an entity an associative array named $D[type]["inhibit"].

If there is an entry of $D[type]["inhibit"] whose first key is ok, save, clone or cancel, the corresponding form button will not be displayed.

Moreover, if there is an entry of $D[type]["inhibit"] whose first key defines the type of list, as described in the Section called The Button Hook, and whose second key is new, modify, delete, select, cancel, add, remove, apply, reset, or chown, then the corresponding button will not be displayed.

Inhibition can be set in the default customisation file. The key chain is as above, but you should use "*" for the table name. Thus, for instance, $D["*"]["inhibit"]["clone"] eliminates the cloning button from all forms, whereas $D["*"]["inhibit"]["sel"]["new"] eliminates the new element button from all selection lists.

Example 4. User front-end

Our librarian wants the users to be able to browse the database using ERW. Of course, users should be able to scan the available books, and maybe some custom button should make them able to print data about them, but we do not want to confuse them with other buttons. This can be obtained by adding the following to the customisation file for the entity book:


$D["book"]["inhibit"]["list"]["new"] = true;
$D["book"]["inhibit"]["list"]["modify"] = true;
$D["book"]["inhibit"]["list"]["delete"] = true;

Note that using true as a value is purely incidental—any other (logically true) value would have done the job.

The Form-Loading Hook

When creating a form for editing, ERW must decide whether it is the case to provide the user with a custom form. The chosen custom form depends on a key (by default, the name of the type of the element), which however can be set depending, say, on the user, using the form-loading hook.

The form-loading hook is activated by assigning to the variable $D[type]["loadForm"] the name of a PHP file: the string returned by the file will be used as a key to load the form (see the Section called Form Resolution).