Code Corner 4 Hooks And The Sidebox - Nilpferdschaf/Egroupware-Doku GitHub Wiki
Hooks are a software design pattern used to create an easy way to extend existing programs without knowing or editing their source code. An interface for registering new hooks has to be provided by the existing program. The hooks will then be called once necessary. EGroupware defines hooks for multiple purposes. You could, for example, use a hook to create a settings page which will show up under the "official" settings menu. You could also use it to populate the sidebox. This is especially helpful if you want to have multiple pages in your app and make navigating between them fast and easy. You will do all of these things in this tutorial.
To register the sidebox hook, add the following line to the setup.inc.php of your app.
$setup_info ['helloworld'] ['hooks'] = array(
'sidebox_menu' => 'helloworld.helloworld_ui.sidebox_menu'
);It tells EGroupware to call the function sidebox_menu() of class helloworld_ui . From there you can then start populating the sidebox. To register this change with EGroupware, increase the version number of the app,
'version' => '0.003',then go to http://$your_domain/egroupware/setup/, log in, click Manage applications and upgrade helloworld.
Now you need to add the function sidebox_menu() to class.helloworld_ui.inc.php. It will create one menu containing two links and another containing some text, a checkbox and a table.
function sidebox_menu()
{
$appname = 'helloworld';
$title = "Links";
$items = array(
'Input name and position' => egw::link ('/index.php', array(
'menuaction' => 'helloworld.helloworld_ui.index'
)),
'Database controls' => egw::link ('/index.php', array(
'menuaction' => 'helloworld.helloworld_ui.db_controls'
))
);
display_sidebox ($appname, $title, $items);
$title2 = "HTML";
$items2 = array(
array(
'text' => '<br>Not a link',
'nolang' => true,
'link' => false,
'icon' => false
),
array(
'text' =>
'<br><br><label>
<input type="checkbox">HTML object
</label>',
'nolang' => true,
'link' => false,
'icon' => false
),
array(
'text' =>
'<br><br><table>' .
'<tr><th>More</th><th>HTML</th></tr>' .
'<tr><td>1</td><td>2</td></tr>' .
'<tr><td>3</td><td>4</td></tr>' .
'</table>',
'nolang' => true,
'link' => false,
'icon' => false
)
);
display_sidebox ($appname, $title2, $items2);
}display_sidebox() creates the menu. This function takes as parameters the name of you app, a $title for the menu and an array $items containing information about the content of the menu. Inside the array you can either have $key => $value pairs or arrays. The $key => $value pairs encode links, where $value contains the hyperlink and $key is the string shown in the sidebox.
The arrays can encode any HTML you want by using the "text" key. The corresponding value will be copied directly into the pages' HTML code. The other keys that can be used are "nolang", specifying wether EGroupware should try translating the value of "text", "link" can be a url the item should link to and "icon" can point to an image that will be displayed next to the item.
Another new feature that is used in the sidebox code is the egw::link() function. It takes a string containing a base url and an array specifying $_GET $key => $value pairs. It will then return a link to the url with all the parameters concatenated to the end.
As you can see, the second link points EGW to a function db_controls() inside helloworld_ui. Go on and add this function. Do not forget to include the corresponding entry in $public_functions.
public $public_functions = array(
'index' => true,
'db_controls' => true
);
function db_controls()
{
$tmpl = new Etemplate ('helloworld.db_controls');
$tmpl->exec('helloworld.helloworld_ui.db_controls', array());
}The code expects a template helloworld.db_controls, so go to the eTemplate editor and create it. Add the controls for removing and editing entries in the database to this page and remove them from the index template.

Move the code that manipulates the database from index() to db_controls().
function index($content = null)
{
$departments = $this->bo->get_department_names ();
if (is_array ($content))
{
$content ['debug'] = '';
$this->bo->insert_employee
(
$content ['fname'],
$content ['sname'],
$content ['position'],
$content ['dep_id']
);
$content ['debug'] .=
"\nHello " . $content ['fname'] . " " . $content ['sname'] .
". You are working as " . $content ['position'] .
" in " . $departments [$content ['dep_id']] . ".";
}
else
{
$content = array();
}
$selection_options = array(
'dep_id' => $departments
);
$tmpl = new Etemplate ('helloworld.index');
$tmpl->exec ('helloworld.helloworld_ui.index', $content, $selection_options);
}
function db_controls($content = null)
{
$departments = $this->bo->get_department_names ();
if (is_array ($content))
{
$select = $content ['select'];
$change = $content ['change'];
if ($content ['delete'] == 'pressed')
{
if ($this->bo->delete_employee ($select ['fname'], $select ['sname']))
{
$content ['debug'] .=
$select ['fname'] . " " . $select ['sname'] . " was removed.";
}
}
if ($content ['modify'] == 'pressed')
{
if ($this->bo->update_employee
(
$select ['fname'],
$select ['sname'],
$change ['fname'],
$change ['sname'],
$change ['position'],
$change ['dep_id']
))
{
$content ['debug'] .=
$select ['fname'] . " " . $select ['sname'] . " was changed.";
}
}
$content ['debug'] .= $this->bo->get_formatted_employee_list ();
}
else
{
$content = array();
$content ['debug'] = $this->bo->get_formatted_employee_list ();
}
$selection_options = array(
'change' => array(
'dep_id' => $departments
)
);
$tmpl = new Etemplate ('helloworld.db_controls');
$tmpl->exec ('helloworld.helloworld_ui.db_controls', $content, $selection_options);
}Now it is finally time to load up EGroupware and experience everything you have done in action. Clicking the links should take you from the main page to the db control page and vice versa.

The database controls should probably not be visible to regular users. If you want to show it only to admin users, surround the code that adds the entry to the sidebox with a check for admin rights.
function sidebox_menu()
{
$appname = 'helloworld';
$title = "Links";
$items = array();
$items ['Input name and position'] = egw::link ('/index.php', array(
'menuaction' => 'helloworld.helloworld_ui.index'
));
if ($GLOBALS ['egw_info'] ['user'] ['apps'] ['admin'])
{
$items ['Database controls'] = egw::link ('/index.php', array(
'menuaction' => 'helloworld.helloworld_ui.db_controls'
));
}
display_sidebox ($appname, $title, $items);
$title2 = "HTML";
$items2 = array(
array(
'text' => '<br>Not a link',
'nolang' => true,
'link' => false,
'icon' => false
),
array(
'text' =>
'<br><br>'.
'<label><input type="checkbox">HTML object</label>',
'nolang' => true,
'link' => false,
'icon' => false
),
array(
'text' =>
'<br><br>'.
'<table>' .
'<tr><th>More</th><th>HTML</th></tr>' .
'<tr><td>1</td><td>2</td></tr>' .
'<tr><td>3</td><td>4</td></tr>' .
'</table>',
'nolang' => true,
'link' => false,
'icon' => false
)
);
display_sidebox ($appname, $title2, $items2);
}Log in with a different account that does not have admin privileges to see wether it worked. The account should have access to your app, of course.

There is a whole range of other hooks you can use. The most important ones are:
$setup_info['helloworld']['hooks'] = array(
//Populate the sidebox menu
'sidebox_menu' => "helloworld.helloworld_ui.sidebox_menu",
//Create settings menu in EGroupwares settings link
'settings' => "helloworld_hooks::settings",
//Verify settings once user is done editing them
'verify_settings' => "helloworld_hooks::verify_settings",
//Access to the admin sidebox, where administration-settings can be changed
'admin' => "helloworld_hooks::admin",
//Access Control List. Determine who can do what.
'acl_rights' => "helloworld_hooks::acl_rights",
//If you want to use categories
'categories' => "helloworld_hooks::categories",
//What to do if a user account is deleted
'deleteaccount' => "helloworld_hooks::deleteaccount"
);You can add this code to your setup/setup.inc.php. Sometimes your hook functions do not need to be part of an object instance and should just be static. In that case, you should make a new class helloworld_hooks inside inc/class.helloworld_hooks.inc.php that contains all your hook functions in one place.
What follows is a quick and basic description of each of these, including some sample code.
If you want to use any of them, you should not forget to add them to the $setup_info['helloworld']['hooks'] array and upgrading the app on the setup page.
If your user account has access rights to the Preferences app, there will be a button with the equivalent name at the top of each page. Because you have not defined any app-specific settings yet, clicking on it right now will take you to the general preferences page. If you want to allow user specific settings in your app, you will need to implement the settings() hook. The function is expected to return an array specifying how to populate the settings menu. A sample implementation is shown below.
static function settings()
{
$sel_options = array(
'a' => 'A',
'b' => 'B',
'c' => 'C'
);
$multi_sel_options = array(
'1' => '1',
'2' => '2',
'3' => '3'
);
$prefs = array();
$prefs ['section 1'] = array(
'type' => 'section',
'title' => 'Section 1',
'no_lang' => true,
'xmlrpc' => False,
'admin' => False
);
$prefs ['selection'] = array(
'type' => 'select',
'label' => 'Favourite letter:',
'name' => 'sel',
'values' => $sel_options,
'help' => 'This is a selection option',
'xmlrpc' => True,
'admin' => False,
'default' => 'b'
);
$prefs ['multiselection'] = array(
'type' => 'multiselect',
'label' => 'Favourite number:',
'name' => 'mulsel',
'values' => $multi_sel_options,
'help' => 'This is a multi selection option',
'xmlrpc' => True,
'admin' => False,
'default' => '1,3'
);
$prefs ['section 2'] = array(
'type' => 'section',
'title' => 'Section 2',
'no_lang' => true,
'xmlrpc' => False,
'admin' => False
);
$prefs ['text_input'] = array(
'type' => 'input',
'size' => 10,
'label' => 'Favourite instrument:',
'name' => 'favourite_instrument',
'help' => 'This is a text input option',
'default' => 'tuba',
'xmlrpc' => True,
'admin' => False
);
$prefs ['checkbox_option'] = array(
'type' => 'check',
'label' => 'Enable bugs',
'name' => 'checkbox',
'help' => 'This is a boolean option',
'default' => 'false',
'xmlrpc' => True,
'admin' => False
);
return $prefs;
}Do not forget to increase the version number and to add an upgrade function. Then go to setup to upgrade your app. Once you are back, you should be able to see these settings options if you click on the Preferences button.

Before the settings are saved, you might want to verify them in case you have some potentially conflicting options. Use the verify_settings() hook for this.
static function verify_settings($data)
{
if ($data ['prefs'] ['favourite_instrument'] == "mayonnaise")
{
egw_framework::message ("Mayonnaise is not an instrument", "error");
}
if ($data ['prefs'] ['favourite_instrument'] == "horseradish")
{
egw_framework::message ("Horseradish isn't an instrument either", "error");
}
}
To access the settings, you can read from $GLOBALS['egw']->preferences->data[$appname]. One of the settings is for the user's favourite instrument. If you append the code in sidebox_menu(), you can show this setting.
//Get settings for favourite instrument
$fav_instrument =
$GLOBALS['egw']->preferences->data['helloworld']['favourite_instrument'];
$items2[] = array(
'text' => "<br>Favourite Instrument: $fav_instrument",
'nolang' => true,
'link' => false,
'icon' => false
);You can now set $favourite_instrument to anything you like, e.g. "accordion" and it will show up in the sidebox.

Using this hook, you can specify admin specific settings that will only be shown in the Admin app's sidebox. It works similar to the sidebox_menu hook in that you can use the same functions and formats.
static function admin()
{
$items = Array(
'Site Configuration' => egw::link ('/index.php',
'menuaction=admin.uiconfig.index&appname=helloworld'),
'Custom fields' => egw::link ('/index.php',
'menuaction=admin.customfields.index&appname=helloworld'),
'Global Categories' => egw::link ('/index.php',
'menuaction=admin.admin_categories.index&appname=helloworld'),
'Main page' => egw::link ('/index.php',
'menuaction=helloworld.helloworld_ui.index')
);
display_section ('helloworld', 'Hello World', $items);
}If you now go to the Admin app and open the Applications tab, you will see the entry for helloworld that you have just defined.

ACL stands for Access Control List. Using this hook you can tell EGroupware of all restricted data your app might save. EGroupware will then handle the entire problem of who is allowed access to what data in which way. This hook takes a parameter containing information about the current user and the location from which the hook was called. In return it expects an array of $key => $value pairs, where the key is an access right identifier and value is a string representation of its meaning in context of your app.
static function acl_rights($params)
{
return array(
acl::READ => 'read data',
acl::ADD => 'add data',
acl::EDIT => 'edit data',
acl::DELETE => 'delete data',
acl::CUSTOM1 => 'sell data',
);
}Now you can click on Access in the top menu, right next to Preferences. Click Add and select helloworld. You can now determine who can do what to whose data based on your custom access controls.

To make use of EGroupwares category system, just use this hook and return true.
static function categories() {
return true;
}Clicking on Categories in the top menu now opens an interface where you can add, edit and delete custom categories specific to your app.

Finally there is the delete account hook. It gets called whenever a user account gets deleted. There is no example code for this as its implementation depends highly on the functions of your app. In general you should do some cleanup here. Delete any data you might have stored about the user and make sure you are not causing any database inconsistencies in the process.
Your new knowledge of the different hooks in EGroupware gives you access to all the pre-implemented standard features, like the settings menu or navigation through the sidebox. This is great for you, because you do not need to manually implement these features, but also great for the users, because they have standardized places to look for them. The user interface is now complete, but it still looks and behaves pretty basic. The following sections will show you how you can change this, beginning with an introduction to the NextMatch widget.
You can download the result of this section here.