mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-21 23:43:13 +01:00
Updated CodeCorner3 (markdown)
parent
6357403896
commit
831c9380b3
380
CodeCorner3.md
380
CodeCorner3.md
@ -1,4 +1,382 @@
|
||||
> [Wiki](Home) ▸ [Developer Docs](Developer Docs) ▸ [Code Corner](Code Corner) ▸ **day three** ▸ [a day in between](CodeCorner3a)
|
||||
|
||||
***
|
||||
some stuff
|
||||
on day three we went out of base camp eGroupware and stepped into the high fields of ((eTemplate))s.<br />
|
||||
|
||||
<h3>motivation</h3>
|
||||
|
||||
<ul>
|
||||
<li>splitting design and layout</li>
|
||||
<li><strong>there is a dialog-editor (one part of the etemplate-app) to create the eTemplate</strong></li>
|
||||
<li><strong>XSS security</strong></li>
|
||||
<li><strong>no parsing of $_POST needed</strong></li>
|
||||
<li><strong>no HTML knowledge needed</strong></li>
|
||||
<li><strong>eTemplates can be (and are usually) nested, eg. a template-field can contain an other eTemplate </strong></li>
|
||||
<li><strong>language independent interfacing:</strong> each field / cell of the template can have a label which is automaticaly run through lang() (the content of the field can be run through lang() too)
|
||||
<ul>
|
||||
<li>lang: the 2 or 5 letter language code (or empty for a non-language specific template)</li>
|
||||
<li>the dialog editor can write all labels in a lang-file (merging it with the existing ones)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>eTemplates have a name of the form app.function[.subtemplate] which is used to call them up</li>
|
||||
<li>version: VersionNumbers like: '0.8.015'</li>
|
||||
<li>they can have further keys, on loading the class picks the most appropriate one for a user:</li>
|
||||
<li>group: the id of a group if the template is just for that group (that allows admin to show differnt views to each group)</li>
|
||||
<li>template set: they belong too (if the template-set key is empty it is an default-template) they are stored in an array and in serialized form in the db-table 'egw_etemplate'</li>
|
||||
<li>the dialog editor can dump all templates of an app for distribution (so they can be in the CVS too)</li>
|
||||
<li>they encapsulate differnt UI (User Interface) types from the app: at the moment only a HTML one is ready, but a GTK one (using php-gtk, running as native app under linux and win32) and XUL is under development.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>how to Setup our application the eGroupware way</h4>
|
||||
There is a [http://egroupware.org/egroupware/etemplate/doc/etemplate.html HowTo] for ((eTemplate))s as well and a [http://egroupware.org/egroupware/etemplate/doc/reference.html reference] too.<br />
|
||||
The [http://egroupware.org/egroupware/etemplate/doc/etemplate.html HowTo] was helpful to get my first moves with ((eTemplate)) going, but we agreed to rewrite that part at least partially.<br />
|
||||
<br />
|
||||
Lets consider we want to rebuild our old flagship <tt>test</tt>. Remember the folderstructure?!
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<pre>
|
||||
test that has to be identical to our app-name
|
||||
+ setup files necessary for the setup Programm, give the webserver write-permission to that dir (on a development box only!)
|
||||
+ 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
|
||||
</pre>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" /><br />
|
||||
The optional file <tt>setup.inc.php</tt> in folder <tt>$app/setup</tt>?<br />
|
||||
<tt>$app/setup/setup.inc.php</tt> should contain the information to setup your application.
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<pre>
|
||||
<?php
|
||||
$setup_info['test']['name'] = 'test';
|
||||
$setup_info['test']['title'] = 'Test';
|
||||
$setup_info['test']['version'] = '0.9.001'; //anything you like, as long as it is fitting the schema of a version number
|
||||
$setup_info['test']['app_order'] = 100; // at the end
|
||||
// $setup_info['test']['tables'] = array('egw_test'); // if there are any
|
||||
$setup_info['test']['enable'] = 1;
|
||||
|
||||
/* Dependencies for this app to work */
|
||||
// if you define dependencies, you MUST meet them to get that baby on the road
|
||||
$setup_info['test']['depends'][] = array(
|
||||
'appname' => 'phpgwapi',
|
||||
'versions' => Array('1.8','1.9'),
|
||||
);
|
||||
$setup_info['test']['depends'][] = array( // this is only necessary as long the etemplate-class is not in the api
|
||||
'appname' => 'etemplate',
|
||||
'versions' => Array('1.6','1.8','1.9'),
|
||||
);
|
||||
|
||||
</pre>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" /><br />
|
||||
Since we had our original application registered manually, we should unregister our baby, to avoid version-number conflicts that may occur, since the manually registered application had no ((VersionNumbers|version number)) at all.<br />
|
||||
So
|
||||
<ul>
|
||||
<li>unregister the manually registered test application</li>
|
||||
<li>log out of eGroupware</li>
|
||||
<li>enter the setup of eGroupware in <tt>egroupware/setup</tt></li>
|
||||
<li>enter the required login information at <strong>Setup/Config Admin Login</strong></li>
|
||||
<li>check with the <strong>Step 4 - Advanced Application Management </strong></li>
|
||||
<li>click on <strong>Manage Applications</strong></li>
|
||||
<li>look for our test application, check with install</li>
|
||||
<li>click on <strong>Save</strong></li>
|
||||
<li>if there are any errors, and there should be none (if you use eGroupware 1.3.012), ...
|
||||
<ul>
|
||||
<li>try to fix them.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>log out</li>
|
||||
<li>Go back to user-login</li>
|
||||
<li>Apply sufficient rights for the application (This is done via the Admin dialog within eGroupware.)</li>
|
||||
<li>call the test application.</li>
|
||||
</ul>
|
||||
It is still there nothing changed so far exept the way we handle the setup.<br />
|
||||
With the <tt>setup.inc.php</tt> we tell eGroupware how to handle our application, apply VersionNumbers and all the other fancy stuff we might need later on.<br />
|
||||
|
||||
<h4>creating an eTemplate for the dialog</h4>
|
||||
Now we need a nice edit dialog and use the eTemplate editor to set it up.<br />
|
||||
Assume that we wanted to name our first eTemplate <em>index</em>.<br />
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 1</dt>
|
||||
<dd>start the etemplate app and type 'test.index' in the name field. Click on save in order to create the template as a new (almost empty) one.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 2</dt>
|
||||
<dd>an (almost empty) empty template is displayed. An eTemplate can be thought off as a “grid”. The first cell may be a bit tricky to find, but will be highlighted when moving the mouse over it. On my computer this cell appears in pink as illustrated below. If you do not see anything, try selecting everything via <tt>Strg-A</tt>. You should see a small rectangle right below the horizontal rule below the button CSS-Style:</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>[vfs:/home/Manual/etemplate0.png]</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 3</dt>
|
||||
<dd>doubleclick the small rectangle right below the horizontal rule below the button CSS-Style. You should see something similar to this:</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>[vfs:/home/Manual/etemplate1.png]</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 4</dt>
|
||||
<dd>as type choose <em>Text</em>, as label <em>name</em>, as name <em>name</em>, add a blurText of your choice and maybe a help. You may set the dimensions of the textfield by setting the options to <em>20,40</em> (approximately 20 character visible, 40 character in available width):</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>[vfs:/home/Manual/etemplate2.png]</dd>
|
||||
<dd>Save your work.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 5</dt>
|
||||
<dd>now we need a button to send our data that we collect to us. To accomplish this, we have to add a column to our grid.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>Doubleclick on the label of our newly created name-text-field.</li>
|
||||
<li>in the upper area of the properties window is a bar with three select boxes. Within the third one select <em>insert a column behind</em>.</li>
|
||||
<li>Doubleclick on the label of our name-text-field.</li>
|
||||
<li>in the third line of the properties window, klick on the tiny right-arrow behind the word <em>grid</em>.</li>
|
||||
<li>choose <em>Submitbutton</em>, as label <em>submit</em>, as name <em>submit</em></li>
|
||||
<li>Save</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 6</dt>
|
||||
<dd>Create a widget before the grid</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>Doubleclick on the label of our name-text-field.</li>
|
||||
<li>Click on the word <em>grid</em></li>
|
||||
<li>in the upper area of the properties window is a bar with two select boxes. Within the second one select <em>insert a widget before</em>.</li>
|
||||
<li>select <em>grid</em> as type</li>
|
||||
<li>Save</li>
|
||||
<li>doubleclick the small rectangle right below the horizontal rule below the button CSS-Style (that is the newly created grid, first cell).</li>
|
||||
<li>select <em>text</em> as type, <em>Hello</em> as label and <em>who</em> as name.</li>
|
||||
<li>disable the cell within the grid-row attributes, by typing <tt>!@who</tt> in the disabled property</li>
|
||||
<li>check the checkbox <em>readonly</em> to make it an read-only textfield. This has two effects:
|
||||
<ul>
|
||||
<li>for one thing the value of @who is not editable</li>
|
||||
<li>the borders around the text-field vanish.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Save</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 7</dt>
|
||||
<dd>Now our first eTemplate is designed.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>we have a input-field to enter text</li>
|
||||
<li>we have a submit button to send our form somewhere (by default back to our very page).</li>
|
||||
<li>we have a container to hold our response</li>
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
ETemplate handles the complete form stuff for you. Data validation, parsing of the <tt>$_POST[...]</tt><br />
|
||||
security issues, callbacks (e.g. for required field values, etc.
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dd>[vfs:/home/Manual/etemplate3.png]</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
Doing all that, all you have to do, is handling the data.<br />
|
||||
The data are transported within the $content[...] array. Each name you assign within a eTemplate will respond to named entrys in the $content[...] array. Our name will be accessible via <tt>$content['name']</tt> and "<em>who</em>" the field which was intended to hold and display the value of the submitted name via <tt>$content['who']</tt>.<br />
|
||||
While you control the content of the <tt>$content[...]</tt> array, you control the behavior of your eTemplate.<br />
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 8</dt>
|
||||
<dd>In order to get your newly created eTemplate <strong>test.index</strong> going, you must call it from within your application. I realized that, from within a function.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
This function is added to the <em>class.test_ui.inc.php</em> file.
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<pre>
|
||||
function index($content=null)
|
||||
{
|
||||
$debug=false;
|
||||
if (is_array($content)){
|
||||
$debug=true;
|
||||
// after submit
|
||||
$content['message']=print_r($content,true);
|
||||
$content['datetime']=time();
|
||||
$content['who']=$content['name'];
|
||||
$content['name']='';
|
||||
$content['ergebnis']=$content['wert1']*$content['wert2'];
|
||||
} else {
|
||||
// first call
|
||||
/*
|
||||
$content=array(
|
||||
'who'=>', please type a name ...',
|
||||
);
|
||||
*/
|
||||
}
|
||||
$tpl=new etemplate('test.index');
|
||||
$tpl->set_cell_attribute('debuginfos','disabled',!$debug);
|
||||
$tpl->exec('test.test_ui.index',$content);
|
||||
// the debug info will be displayed at the very end of the page
|
||||
//_debug_array($content);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 9</dt>
|
||||
<dd>Since you want to be able to call that function from the eGroupware as function call or link or redirekt-link, you have to add the function name to the public_functions array.</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<pre>
|
||||
var $public_functions = array(
|
||||
'testinterface' => True,
|
||||
'index' => True,
|
||||
);
|
||||
</pre>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 10</dt>
|
||||
<dd>You have to add a constructor to your test_ui class:</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<pre>
|
||||
// construktor
|
||||
function test_ui()
|
||||
{
|
||||
$GLOBALS['egw_info']['flags']['app_header']='test-Application';
|
||||
|
||||
$this->tmpl =& CreateObject('etemplate.etemplate', 'test.index');
|
||||
$this->bo =& CreateObject('test.botest');
|
||||
$this->html =& $GLOBALS['egw']->html;
|
||||
|
||||
if(!@is_object($GLOBALS['egw']->js))
|
||||
{
|
||||
$GLOBALS['egw']->js =& CreateObject('phpgwapi.javascript');
|
||||
}
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
<hr style="text-align: left; width: 99%;" />
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 11</dt>
|
||||
<dd>If we add <tt>echo "<a href='".$GLOBALS['egw']->link('/index.php',array('menuaction' => 'test.test_ui.index'))."'> Call the eTemplate version </a> <br>";</tt> to our <em>old</em> function <em>testinterface</em> just before the creation of the footer, we have a link to start up our new eTemplate.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd>Sure thing, you can call your eTemplate - which is wrapped in a function from your test/index.php:</dd>
|
||||
<dd><tt>header('Location: ../index.php?menuaction=test.test_ui.index');</tt></dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
<dl>
|
||||
<dd>
|
||||
<dl>
|
||||
<dt>Step 12</dt>
|
||||
<dd>If you wanted to have some fancy title for your Application, different from your application name. Set the <strong>app_header</strong> (as I did) within the constructor:</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dd><tt>$GLOBALS['egw_info']['flags']['app_header']='My fancy application title';</tt></dd>
|
||||
</dl>
|
||||
</dd>
|
||||
</dl>
|
||||
<br />
|
||||
Now we have our application converted to the eTemplate way of eGroupware. As you can see in the code of the function, I added a few fields to the eTemplate. You can see that I handle data of the <tt>$content[...]</tt> array and assign them to fields that I have not talked about.<br />
|
||||
<br />
|
||||
The index function is called from our index.php file, from a link out of our old application 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.<br />
|
||||
|
||||
<h4>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)</h4>
|
||||
|
||||
|
||||
<ul>
|
||||
<li>the $content array is set up with our internal data-array (which is empty on the first call) and the message</li>
|
||||
<li>$readonlys: if a fieldname is set in $readonlys to True, its content is displayed readonly (for regular fields like type Text) or left out for buttons (we use this later)</li>
|
||||
<li>at last we call etemplate::exec to show the template with the content from $content and set the function itself as callback for the dialog / form.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>Now let's have a look what happens if the user submits the form and our callback is called.</h4>
|
||||
|
||||
|
||||
<ul>
|
||||
<li>the callback (this function) is not the submit-address of the form, the form get's always submitted to the function process_exec of the etemplate class. This function changes for some field-types the content (eg. a date-field consists of 3 single fields, process_exec takes care that it is delivered back as timestamp, as we set it in content before). It can even submit the form back to the user. In this case the callback is NOT called. The same is true if an int field contains letters or is not within the minimum or maximum set. <em>For the specialist process_exec uses $_POST and ignores $_GET set as query in the url. </em></li>
|
||||
<li>if <tt>$content['submit']</tt> is set, the [Submit] button has been pressed ('submit' is the name NOT the label of the Submit button).</li>
|
||||
<li>after that the content array is filled again as described above.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>What do we have</h4>
|
||||
Using the eTemplate we have now splitted the display of the userinterface from its processing code.<br />
|
||||
The structured developing techniques of the CodingRules - the 3-tier-approach (<em>UI > BO > SO !!!</em>) - enables us to split the layers of our application, and handle certain stuff at the right level (e.g. time -> user/system) and just there.<br />
|
||||
|
Loading…
Reference in New Issue
Block a user