Changes made to the application framework that the application builder should know: =========================================== -------- constructing an HTML page ----------- * - WebApp::getHtmlPage($tpl) Constructs the page given as a parameter and returns it as an HTML string. It saves whatever is constructed in the current page and then resumes it again after constructing the requested page, so it can be called from anywhere in the application, e.g. from an event handler. This function is usefull when you want to construct a page for sending it by e-mail. -------- external link to an application page ----------- * - The function WebApp::external_page() returns a link that can be used to jump to this page of the application from any external page. Also the constant "SHOW_EXTERNAL_LINK", which is defined in 'config/const.Settings.php' makes possible to display the external link to the end of the page. The external links are useful e.g. for generating an index page for search engines, so that they can access and index the content pages of the application. -------- the PHP code of a webox ----------------------- * - The PHP code of a webox: Vars["state_var"])) { $session->Vars["state_var"] = "initial_value"; } Now it should be done in the init() function, like this: $this->addSVar("state_var", "initial_value"); ----- state vars (or session or persistent vars) added to webox ---- * - Now each webox can have its own state vars (or session vars, or persistent vars). Inside the class of the webox, its state vars can be accessed like this: $this->addSVar ($var_name, $var_value); $this->addSVars($associated_array); $this->setSVar ($var_name, $var_value); $this->setSVars($associated_array); $var_value = $this->getSVar($var_name); $assoc_arr = $this->getSVars(); These variables can be accessed inside the webox in the usual way: {{var_name}} and they are valid only in the scope of this webox (and shadow any other {{variables}} with the same name that are defined in the enclosing scopes. In case that some state variable of a webox needs to be used from the template of another webox, they can be accessed like this: {{boxID->var_name}} From the JS code as well, they can be accessed like this: session.setVar("boxID->var_name", var_value); etc. Also, when you need to access the state var of another box from the PHP code of this webox, you can use this function: $var_value = WebApp::getSVar("otherBoxID->var_name"); The function $var_value=WebApp::getSVar($var_name) can also be used to get the session variables (instead of $var_value=$session->Vars[$var_name]). Also the function: WebApp::setSVar($var_name, $var_value) is available (instead of $session->Vars[$var_name] = $var_value;), and the function: $wb = WebApp::getWBox("wbox_id"); which returns a reference to the webox with the given id (this reference can be used to access the variables, state variable or functions of this webox, e.g. $wb->getQuery(); ). If the constant DEBUG_STATEVARS in the 'config/const.Settings.php' is set to true, then the framework outputs the state variables for each webox. ------- MISC ------------------------------------------------ * - Variables can also be used in the 'rs' attribute of a tag, e.g. . . . . This allows a repeat block to use different queries in different cases (according to the logic of the program). These variables are evaluated at the parse time, at the time that the repeat element is parsed. So, if you give value to this variable inside the function onRender(), it will not get it; instead it should be given value in the function onParse(), or it should be a state variable or global variable. * - Variables can be nested, like this: {{checked_{{id}}}} First the innermost variable is replaced, then the outer variable. If {{id}} has the value "2" and {{checked_2}} has the value "true", then the whole variable above will be replaced by "true"; If the value of a variable contains another variable inside, it will be replaced as well. E.g, if the variable {{nr}} has the value "5" and the variable {{msg}} has the value "This is message number {{nr}}.", then using {{msg}} inside a template will produce the string: "This is message number 5." * - The framework defies a constant UNDEFINED (which has the value "UNDEFINED" or something else) and is used in the framework and in the application whenever a variable has no value. E.g. if the function WebApp::getWBox("wb_id") returns UNDEFINED, it means that such a webbox does not exist in the application, or if the function Recordset::Field($fld_name) returns UNDEFINED, it means that such a field does not exist or it has no value, etc. * - The callback function onLoad() is renamed to onParse(). As before, it is called just before the webbox is parsed. ------------------ editForm ------------------------------------------- * - Added these new JS functions (in editForm.js): function getFormData(form) //returns all the data filled in the given form function setFormData(form, formData) //fills the form with the data in the given array, //this is the array returned by getFormData() for the same form function getEventArgs(formData) //returns a string that can be sent as the //arguments of an event in the GoTo() function //formData is the array returned by getFormData() This will be improved further, so that it becomes more general and more easy to use. ----------------- Session ------------------------------------- * - The special variable 'sessChange' is removed as obsolete. The programer can change the session on client side, before calling GoTo(), instead of changing session variables using 'sessChange'. * - 'init.Session.php' is removed as obsolete. The session (state) variables can be initialized either in the 'init()' function of the weboxes or in the 'on.firstTime.php'. * - Added session.rmVar(var_name) so that session vars can be removed from JS code as well. The names of the session functions in JS have been changed so that they are more consistent with the other function names in framework. Now they are: session.addVar("var_name", "var_value"); session.rmVar("var_name"); session.setVar("var_name", "var_value"); session.getVar("var_name"); * - The names of the functions of the session object are: $session->addVar($var_name, $var_value); $session->rmVar($var_name); //removes a variable from session $session->setVar($var_name, $var_value); //sets a new value $session->getVar($var_name); //get the value of the var The names of the functions for the state vars of a webox are: $this->addSVar($var_name, $var_value); $this->addSVars($arr_vars); $this->setSVar($var_name, $var_value); $this->setSVars($arr_vars); $this->getSVar($var_value); $this->getSVars(); The session and state vars can also be accessed using this functions: WebApp::addSVar($var_name, $var_value); WebApp::setSVar($var_name, $var_value); WebApp::getSVar($var_name); * - DB Session Vars (or DB State Vars) Some of the state (session) variables now can be optionally stored in DB instead of storing them in the HTML page itself (using JS code). This increses the security of an application based on the framework, because the variables that are stored in the JS code potentially can be viewed and manipulated by malicious users. So, some of the session vars can be stored in DB and they are invisible on the client side, and the others (most of them) can be stored in the page, so that they can be accessed and used by the JS code of the application. To add a DB var, we use the same functions that are used to add a JS var, but add a third parameter that is not false, like this: $session->addVar($var_name, $var_value, true); $session->addVar($var_name, $var_value, "DB"); $session->addVar($var_name, $var_value, "secure"); etc. $this->addSVar($var_name, $var_value,"DB"); WebApp::addSVar($var_name, $var_value, "DB"); To change or get the value of a DB variable we use the same "set" and "get" functions that we use for JS vars, without additional parameters (the framework finds itself whether this is a DB or a JS state variable). Usually a DB and a JS var have different names, but in case that they have the same name, DB vars have priority over JS vars (which means that "get" returns the value of the DB var and "set" sets the value to the DB var, instead of to the JS var). The array: '$session->dbVars' can be used to access the DB vars as well, like this: $session->dbVars[$var_name] = $var_value; $var_value = $session->dbVars[$var_name]; but it is not neccessary and it is not recomended to use it (in fact, it is discouraged to use it). The function $this->getSVars() behaves a little bit different from the others. Without argument it returns a list of all the state vars of the webox, both DB vars and JS vars. With an argument "DB" it returns only the DB vars, and with argument "JS" it returns only the JS vars. For DB vars to work properly, there should be a DB connection with a database and the USES_DB constant in 'config/cont.Settings.php' must be true. Otherwise the DB vars are converted and stored as JS vars. Also, in database there must be the table session(id, vars), but if it does not egzist, it is created automatically by the framework (if the connection has permition to create tables). ------------------ event handling ------------------------------- * - The class of a webox can declare an event handler function, with the name "on_eventName". If such a function exists, then this function is called to handle the event, otherwise the function eventHandler($event) is called, as before. This is useful both for backward compatibility and for flexibility: the programer chooses how to handle the events, either with a separate event handler for each event, or with a common event handler for all the events. ------------------- WebApp::message() --------------------------- * - The function: WebApp::message("msg") can be used in PHP code to display a message to the user after the page is loaded in browser. This can be used to display an error message, e.g. "Wrong username or password", or an information message, e.g. "Project Data Saved". ---------------- session var values ---------------------------- * - The session JS vars can contain: ' (single quote), " (double quote), \n (new line character), \ (slash), \t (tab), etc. They cannot contain, however, \' (slash single quote), \" (slash double quote), \n (slash n). ---------------- Recordsets ------------------------------------ * - Satic Recordsets: These are recordsets that are opened only once during the page construction. Usually the recordsets are opened as many times as they are needed (i.e. their query is executed and the results retrieved from DB). This has the advantage that the query can be different each time that the recordset is opened (depending on the {{variables}} that it contains), and this provides for more flexibility and dynamicity. However, in some cases, the same query is executed all the times that the recordset is opened, e.g. when this recordset is used to fill a listbox and this listbox can be 20 times in the page. In this case it is more efficent that the recordset is opened only once and it is used in the 20 cases. * - The syntax of tag Recordset now is: where 'rsType' can be one of the values: "StaticRS", "DynamicRS", "EditableRS", "PagedRS", "TableRS". The attibute type can be omitted; in this case the default value is "StaticRS". A 'static' recordset is the recordset described above, a RS that cannot be opened more than once per page. A 'dynamic' RS is a recordset that evaluates its query (replaces its {{variables}}), executes it and refreshes the content each time that its method Open() is called. Notice that before a recordset was 'dynamic' by default and 'static' only if specified, but now a RS is 'static' by default (if no type is given). This is because 'static' recordsets are more common in web applications and 'dynamic' recordsets are useful only in special cases, e.g. when we have two nested recordsets (associated with two nested s) and the inner RS uses a {{variable}} provided by the outer RS. A 'PagedRS' is a recordset that divides the results of the query into pages of certain size, and retrives from DB only one page of them. It requires the attribute 'recs_per_page', which specifies the size of the page. It is used to display big recordsets, when they cannot fit in a single HTML page. An 'EditableRS' is a RS that can be modified from the PHP code. The programer can retrive the results of a query from DB and then modify them, or start with an empty RS and fill it programatically. It makes possible to use templates for results that don't come from DB (e.g. when you want to display a folder listing and you get the files and folders using some PHP functions). This feature can be used in the "folderListing" webbox, for example. Instead of generating all the html code of the folder listing programatically, a template, associated with a could be used, and this recordset could be filled from an array or from php code (instead of being filled from database). The benefit of this aproach (vs. generatin all the html code in php) would be that we are able to change the layout of the 'folderListing' webbox more easily, because instead of changing the php code, we change only a template. The functions of EditableRS are: $rs->apply($fun_name) //applies the function $fun_name on each record //of the recordset; $fun_name is a global function //that takes as parameter a reference to an associated //array (a reference to a record), like this: // function fun_to_be_applied(&$rec) $rs->setFld($fld_name, $fld_value) //sets a new value to the given field (in the current record) $rs->setFlds($arr_flds) //changes some fields of the current recordset $rs->addRecs($arr_recs) $rs->addRec($rec) //adds a new record after the current record, //the new record becomes current $rs->insRecs($arr_recs) $rs->insRec($rec) //insert a new record before the current record, //the new record becomes the current record $rs->rmRec() //removes the current record $rs->rmRecs($nr) //removes $nr recs from the recordset, //starting with the current record; //if $nr is not given or exeeds the EOF, //then removes all the recs up to the EOF $rs->slice($offset, $length) //returns a new recordset that contains a slice of this recordset; //see the documentation of array_slice of PHP $rs->match($condition, $pos) //returns true if the query at the given position //matches the $condition (usually used by the functions //find() and filter() above); if no position is given //matches the current record; //currently the $condition supports only one field $rs->filter($condition) //returns a new recordset with the recs that match the //given condition, $condition is like the WHERE condition //of a query, but it matches using regexp-s, e.g. //(username=/^d.*/ AND (firstname=/[^a]+/ OR NOT lastname='Hoxha')) //(currently it supports only one field, no AND, OR, (, ) etc.) $rs->find($condition) //finds the subset of the recordset that matches the //condition and positions the pointer to the first //record found; $condition is like the condition in filter(); //if called without parameter, if finds the next one, //and so on up to EOF; $rs->search($condition) //used by find(), finds all the records that match //the condition and stores their indeces in //the array $this->found_positions $rs->find_next() //used by find(), returns the next found record //or UNDEFINED if there are no more records $rs->getColumn($fld_name) //returns an array with the values of the specified field $rs->getColumns($fld_names) //$fld_names is a comma separated list of field names; //returns a new recordset that contains only the specified //columns A "TableRS" is an editable RS which is associated with a table in a DB. After editing this RS, the programer can save the changes back to DB. (This is not implemented yet.) * - WebApp::execQuery($query) now returns an EditableRS. * - Function: WebApp::openTable($table, $condition) added. //opens the given table and returns the records that //satisfy the given $condition; returns a TableRS ---------------------------------------------------------------- * - The constant DB_TYPE in 'config/const.Settings.php' specifies the type of the DB that the application is using, like this: define("DB_TYPE", "MySQL"); //this constant defines the type of DB that the application uses //it can be: "MySQL", "Oracle", "ODBC", "ProgreSQL", etc. //(except "MySQL", the others are not implemented yet) ----------------------------------------------------------------- * - Parser separated from Template. ----------------------------------------------------------------- * - The framework supports WebClasses and WebObjects. The element defines a web class, but it by itself does not produce any HTML output in the rendered page. The element declares an object of the class and the content (the template) of the is used to render this object. The 'Path' attribute of the declares the path of the file that defines the PHP code of the WebClass. It is optional, and if it is not given, then the current folder (the folder of the template that contains the definition) is searched for the file "className.php". The elements declare the names and optionally the default values of the parameters of the webobjects. (The current implementation requires that they come immediately after the line and have no empty lines or other lines between them, otherwise they will not be parsed correctly.) These parameters can be used inside the template of the webclass like template variables, e.g. {{param1}}. If a parameter doesn't get a value in the declaration, then its default value is used (if it doesn;t have a default value, then it is handled like an undefined variable). When a is rendered, the value of the parameters is evaluated as a mixed expression (a PHP expression that contains {{template vars}}). When several objects of the same class are declared, the template of the webclass is repeated for each of them. The webclass designer should take this into account when building the webclass. E.g. if there is such an input in the webclass template: There will be several such inputs in the HTML code, and its value cannot be accessed like this: document.form.staff.value ToDo: The framework declares some variables like: {{className}} {{objName}}, {{objID}} (which usually is {{className}}::{{objName}}), {{objCount}} (counts the objects of the same class, starting from 1), etc. These vars can be used inside the template of the webclass, if the webclass designer needs them, e.g.: The PHP code ------------- The file "className.php", if it egzists, contains this class definition: Where 'WebObject' is a class defined by the framework that supplies some useful functionality for all webclasses. This functionality is: 1 - The callback functions that are called by the framework at certain moments during the time that the webobject is processesed. These callback functions are: + init() + on_eventName($event_args) + eventHandler($event) + onParse() + onRender() 2 - The ability to keep persistent variables that define the state of the object. These state variables can be used inside the "className" like this: + $this->addSVar($var_name, $var_value); + $this->addSVar($var_name, $var_value, "DB"); + $this->setSVar($var_name, $var_value); + $this->setSVars($var_name, $var_value); + $var_value = $this->getSVar($var_name); + $arr_values = $this->getSVars($var_name); + etc. Outside the scope of the WebClass, these state variables can be accessed like this: + WebApp::setSVar("className::objName->var_name", "var_value") + $var_value = WebApp::getSVar("className::objName->var_name"); + {{className::objName->var_name}} (in templates) + session.setVar("className::objName->var_name", "var_value") + etc. WebBox-es --------- The -es are handled by the framework as a special case of -es and -s. When the framework parses: . . . . . it interprets it as: . . . . . and the ID of the created object, instead of being "className::objName" is just "boxID", so it is handled the same as before (e.g. $var_value = WebApp::getSVar("boxID->var_name"), etc. ) Thus, when upgrading an egzisting application, the only change that needs to be done is to replace: class boxID extends IWebBox with class boxID extends WebObject ----------------------------------------------------------------- * - The function: WebApp::debug_msg($msg, $comment); is added to the framework. It makes the framework to display the given message after the page is rendered. The comment is optional. You can also print a debug message like this: print "$comment: $msg
\n"; but the output of this statement may be mixed with the HTML code and sometimes it may break the page, so the first method is safer and cleaner. The message $msg can be a large message that contains tags, e.g. WebApp::debug_msg($tpl->toHtmlTable()); WebApp::debug_msg($rs->toHtmlTable(), "The content of recordset"); WebApp::debug_msg($tplVars->toHtmlTable(), "The content of varstack at this point"); WebApp::debug_msg($webPage->template_list(), "All the templates parsed up to this point"); etc. ----------------------------------------------------------------- * WebApp::openRS($rs_id, $params =array()) WebApp::execDBCmd($cmd_id, $params =array()) These functions now take an optional array of parameters, which are replaced in the query as {{tpl_vars}}. ----------------------------------------------------------------- * $webPage->append_to_head($line); $webPage->append_to_body($line); These functions can be used to add more lines to the content of the and elements of a web page. The first one (that appends to ) cannot be used inside onRender(), because the of the page has been already sent to the browser. -----------------------------------------------------------------