| ERW: The Manual | ||
|---|---|---|
| <<< Previous | Customisation | Next >>> |
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).
![]() | 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:
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.
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.
An associative array with keys select, update, insert and delete that describe the current permissions.
The locale that has been negotiated with the browser.
The language part (the first two letters) of the locale that has been negotiated with the browser.
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 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:
The (entity or relationship) type of the element modified or created by the form currently processed.
The numerical identifier (primary key) of the element that caused the current hook invocation, or 0 in case the element is new.
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.
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.
![]() | 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"; |
<?php global $q; if (strlen(ERW::unquote($q["book"]["title"])) < 2) return "Title too short!"; else return ""; ?> |
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).
![]() | 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.
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 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!";
}
?>
|
$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 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");
|
![]() | This hook is deprecated, and may not be supported in future versions of ERW |
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.
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.
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).
| <<< Previous | Home | Next >>> |
| Custom Files | Up | Custom Forms |