A developers tutorial how to write an application with the new eTemplates.
It is also an introduction how to write a phpgw- and setup(3)-compatible app.
As an example we will run now through the necessary steps to create a simple media database using eTemplates and other tools and classes from the eTemplate app: db-tools and class.so_sql.inc.php.
Out media database should have the usual fields: name, author, description, type: BOOK, CD, VIDEO and should be able to edit records and search for them.
As a preaquistion you need to get / checkout the etemplate app, install the app via setup/manage applications and enable your account for using the app (Admin/User account: check eTemplates).
Each app need a name, eg. 'et_media'. We now need to create the following directory structur above the eGroupWare dir:
et_media that has to be identical to our app-name + setup files necessary for the setup Programm, give the webserver write-permission to that dir + inc class-files + templates templates, still needed to store the images and get around a lot of complains from the api + default + images here goes our images / icons
That files contains the necessary information for setup to install the app.
<?php $setup_info['et_media']['name'] = 'et_media'; $setup_info['et_media']['title'] = 'eT-Media'; $setup_info['et_media']['version'] = '0.9.15.001'; $setup_info['et_media']['app_order'] = 100; // at the end $setup_info['et_media']['tables'] = array('phpgw_et_media'); $setup_info['et_media']['enable'] = 1; /* Dependencies for this app to work */ $setup_info['et_media']['depends'][] = array( 'appname' => 'phpgwapi', 'versions' => Array('0.9.14','0.9.15','0.9.16') ); $setup_info['et_media']['depends'][] = array( // this is only necessary as long the etemplate-class is not in the api 'appname' => 'etemplate', 'versions' => Array('0.9.14','0.9.15','0.9.16') );
To enable setup to create a db-table for us and to supply the so_sql-class with the necessary information, we need to define
the type and size of the fields / columns in our db-table.
We can use the db-Tools from the etemplate app to create the file for us:
Now we need a nice edit dialog and use the eTemplate editor to set it up:
As you see above i added an application titel, a horizontal rule after it and some space (empty label's). Do so if you want.
The index page is only used if someone clicks on the navbar icon (or on the black cross as we haven't supplied one so far).
Create the file /et_media/index.php with the following content:
<?php $GLOBALS['phpgw_info']['flags'] = array( 'currentapp' => 'et_media', 'noheader' => True, 'nonavbar' => True ); include('../header.inc.php'); header('Location: '.$GLOBALS['phpgw']->link('/index.php','menuaction=et_media.et_media.edit')); $GLOBALS['phpgw_info']['flags']['nodisplay'] = True; exit;
As a first step, we only save new entries. The code of the app is in /et_media/inc/class.et_media.inc.php:
<?php /**************************************************************************\ * eGroupWare - eTemplates - Tutoria Example - a simple MediaDB * * http://www.eGroupWare.org * * Written by Ralf Becker <RalfBecker AT outdoor-training DOT de> * * -------------------------------------------- * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License, or (at your * * option) any later version. * \**************************************************************************/ /* $ Id: class.et_media.inc.php,v 1.2 2002/10/19 11:11:03 ralfbecker Exp $ */ include_once(PHPGW_INCLUDE_ROOT . '/etemplate/inc/class.so_sql.inc.php'); $GLOBALS['phpgw_info']['flags']['included_classes']['so_sql'] = True;
This loads the class so_sql, in a way that more than one class may use it (would be nice if we had an api-function for that).
class et_media extends so_sql { var $types = array( '' => 'Select one ...', 'cd' => 'Compact Disc', 'dvd' => 'DVD', 'book' => 'Book', 'video' => 'Video Tape' );
These are a few messages to show the user what happend, we show it via 'msg' in content in the first Label-field after the app-title.
As one of the messages contain a %s to be used with sprintf, we have to run them manualy through lang().
function et_media() { $this->tmpl = CreateObject('etemplate.etemplate','et_media.edit'); $this->so_sql('et_media','phpgw_et_media'); // sets up our storage layer using the table 'phpgw_et_media' $this->empty_on_write = "''"; // what to write in the db, if a column is empty, the default is NULL $this->public_functions += array( // this function can be called external, eg. by /index.php?menuaction=... 'edit' => True, 'writeLangFile' => True ); }
This is the contructor of the class, it does the following for us:
function edit($content='',$msg = '') { if (is_array($content)) // we are called as callback for the dialog / form { if ($content['id'] > 0) // if we have an id --> read the entry { $this->read($content); } $this->data_merge($content); // merge content with our internal data-array ($this->data) if (isset($content['save'])) // save the entry ($this->data) { $msg .= !$this->save() ? lang('Entry saved') : lang('Error: writeing !!!'); } elseif (isset($content['read'])) { unset($content['id']); // not set by user, so dont use for seach $found = $this->search($content,False,'name,author'); // searches by using the no-empty fields if (!$found) // search returned empty { $msg .= lang('Nothing matched search criteria !!!'); } else { $this->init($found[0]); // set data-array with the content of the first match } } } // now we filling the content array for the next call to etemplate.exec $content = $this->data + array( // the content to be merged in the template 'msg' => $msg ); $sel_options = array( // the options for our type selectbox 'type' => $this->types ); $no_button = array( // button not to show ); $preserv = array( // this data is preserved over the exec-call (like a hidden input-field in form) 'id' => $this->data['id'] ); $this->tmpl->exec( 'et_media.et_media.edit', // setting this function as callback for the dialog $content,$sel_options,$no_button,$preserv ); } }
The edit function is called from our index.php file or as callback for this form / dialog. In that case $content is an array with the content the user put into the fields of the dialog.
Let first have a look what happend if we called the first time (or what we do to show the dialog again with the changed data):
Now let's have a look what happens if the user submits the form and our callback is called:
Now we are able to store entries in the db and retrive them by searching the database for patterns in the different fields.
We are only lacking some way to show if we get more than one match on a search, that's what we are going to implement next:
First we need to create an other eTemplate to show the list: 'et_media.show'
As you see the templates includes an other template: 'et_media.show.rows'
We need some code / a function in the class to call the template and fill the content:
function show($found) { if (!is_array($found) || !count($found)) { $this->edit(); return; } reset($found); // create array with all matches, indexes starting with 1 for ($row=1; list($key,$data) = each($found); ++$row) { $entry[$row] = $data; } $content = array( 'msg' => lang('%d matches on search criteria',,count($found)), 'entry' => $entry // et_media.show.rows uses this, as we put 'entry' in the Options-field ); $this->tmpl->read('et_media.show'); // read the show-template $this->tmpl->exec('et_media.et_media.edit',$content); // exec it with the edit-function as callback }
This function is called by edit with the matches of a search:
To call the show function, we need to make some changes to the edit-function too:
elseif (isset($content['read'])) { unset($content['id']); // not set by user, so dont use for seach $found = $this->search($content,False,'name,author'); // searches by using the no-empty fields if (!$found) // search returned empty { $msg .= lang('Nothing matched search criteria !!!'); } elseif (count($found) == 1) // only one match --> show it in the editor { $this->init($found[0]); } else // multiple matches --> use the show function/template { $this->show($found); return; } } elseif (isset($content['entry']['edit'])) // the callback from for the show function/template { // the id is set via the button name of '$row_cont[id]' list($id) = each($content['entry']['edit']); // note its not only ['edit'] !!! if ($id > 0) { $this->read(array('id' => $id)); } }
While makeing this changes we can add a [Cancel] and [Delete] button too:
elseif (isset($content['cancel']))
{
$this->init();
}
elseif (isset($content['delete']))
{
$this->delete();
$this->init();
}
$no_button = array( // no delete button if id == 0 --> entry not saved
'delete' => !$this->data[$this->db_key_cols[$this->autoinc_id]]
);
Of course we have to add this buttons to the template 'et_media.edit'. I trust you can add 2 Submitbuttons with the names 'cancel' and 'delete', a Label and a nice helpmessages by now without looking at a screenshot ;-).
To get rid of the stars '*' behind each Label and to be able to translate the app in other languages we need to create a lang-file
There are 2 possibilties to create it automaticaly:
Anyway we have to use the TranslationTools to find and write the lang()-messages of our code!
/*!
@function writeLangFile
@abstract writes langfile with all templates registered here
@discussion can be called via [write Langfile] in eTemplate editor
*/
function writeLangFile()
{
return $this->tmpl->writeLangFile('et_media','en',$this->types);
}
To be able to put the eTemplates in CVS and to ship them with your app, you need to dump them in a file first.
This is done in the eTemplate editor by putting the app-name or an template-name in the Name field and clicking on the button [Dump4Setup]. This creates the file et_media/setup/etemplates.inc.php. The etemplate-class loads this file whenever it finds a new version automaticaly.