NAME
Zanas - Web application construction set.
DESCRIPTION
Zanas is a set of naming conventions, utility functions, and a basic Apache request handler that help to quickly build robust, efficient and good-looking Web interfaces with standard design. The last doesn't mean that you can't alter hardcoded HTML fragments at all. But building public Web sites with original graphics and layout is not the primary goal of Zanas development. Zanas is good (I hope) for developing client database editing GUIs ('thin clients') and is conditionnally comparable to Windows API and java Swing.
Zanas' basic features are:
- GUI base
-
usable set of HTML widgets (forms, toolbars, etc);
- sessions
-
session management subsystem with transparent query rewriting;
- js alerting
-
server side error handling and data validation with client javaScript notifications without page reloading (yes, it really is);
- logging
-
action logging is a part of core process, additionl API calls aren't needed;
- fake records
-
handling of temporary records that are only visible on creation forms and wizards;
DESIGN PRINCIPLES
There is a whole a lot of univesal web application platforms. So, why develop another one instead of using some mature product? 'Cause we've already tried many of this and have'nt found a good one.
When developing Zanas, we use the following principles:
- no OO
-
HTTP is nothing else than evaluting string functions with sets of named parameters. Request handler must do nothing else than decompose the top function to some more primitive functions. So, Zanas is purely procedure-oriented framework.
- content/presentation separation
-
Request handler reduce the top function f (x) to a superposition of a content and a presentation function: c (x) and p (c, x), where c (x) can't produce any HTML fragment in its result and p (c, x) can't use any info stored in the database.
f (x) = p (c (x), x).
- URL discipline and strict callback naming
-
Content and presentation functions can be reduced to swithes between some elementary callback functions, where the switch is directly governed by known CGI parameters. Say, for
url='/?type=users'
c == select_users
andp == draw_users
. - no ASP
-
Perl is ideal for implementing templating languages. That's why people love to implement new templating languages in Perl. But most of them ignore the fact that Perl is already a templating language. Heredoc syntax is much more usable than any ASP-like. And it doesn't require any additional processing: everything is done by the Perl interpreter.
- no XML
-
Nested Perl datastructures like list-of-hashes and more complex offer the same functionnality as the XML DOM model. And it doesn't require any external libraries: everything is done by the Perl interpreter.
- no XSLT
-
It would be very strange to use XSLT without XML, but we must underline here that there was one more reason to not use XSLT. Its syntax is even much crappier and less flexible than ASP-like.
- no cacheing
-
We claim ourself unable to develop a universal and effective transparent cacheing system with authomatic dependencies tracking. (Can anybody do it?) And we don't need a memory hog with multiple obsolete copies of DB content accessible with some sophisticated API.
MAGIC CGI PARAMETERS
The next CGI parameters have special meaning in Zanas URLs and can be used only as described.
- sid
-
Session ID. If not set, the client is automatically redirected to the logon screen.
- type
-
Type of the current screen. Can have values like
'users'
or, for example,'users_creation_wizard_step_2'
. Influences the callback functions selection and the main menu rendering. - id
-
Current object ID. Influences the callback functions selection. When set, the screen presents detailed info of one object, otherwise, it contains some search results.
- action
-
Name of the action to execute. If set, the request handler executes some editing callback, then evalutes the new URL where
action
is unset and redirects the client there. - salt
-
Fake random parameter for preventing the client HTML cacheing.
GLOBAL VARIABLES
The next variables are accessible in all callback subs.
- %_REQUEST
-
The hash of CGI parameters and its values
- $_USER
-
The hashref containing the current user information:
{ id => ... name => ... role => ... }
CALLBACK SUBS
Under differnent circumstances, Zanas Apache request handler executes appropriate callback subs. The name of the callback to execute depends on current program context, type
value and the role of the current user.
Suppose that the context imply the callback name $my_callback
, $_REQUEST{type}
is $type
and $$_USER {role}
is $role
. In this case, if the sub named "${my_callback}_${type}_for_${role}" is defined, it will be called. Otherwise, if the sub named "${my_callback}_${type}" is defined, it will be called. Otherwise, undef value will be used instead of missing sub result.
In the next sections, "${my_callback}_${type}_for_${role}" always means one of 3 cases described above.
- validate_{$action}_${type}_for_${role}
-
This sub must analyze the values of parameters in
%_REQUEST
hash for consistency. In most cases, the object id is stored in$_REQUEST {id}
and the names of all other fields are underscore prefixed ($_REQUEST {_name}
,$_REQUEST {_login}
,$_REQUEST {_password}
etc).If everythig's OK, the validator must return
undef
. Otherwise, the return value is an error code. We'll call it$error
. So, if$error
is defined, an error message template$$error_messages {"{$action}_${type}_${error}"}
is interpolated as a qq-string and then sent to the user as the error message.For example, if the sub
validate_update_users_for_admin
returns'duplicate_login'
,$_REQUEST {_login} eq 'scott'
and$$error_messages {"update_users_duplicate_login"} eq 'Duplicate login: \'$_REQUEST{_login}\''
, then the error message will be"Duplicate login: 'scott'"
. - do_{$action}_${type}_for_${role}
-
This sub must execute the
$action
. Note that you can choose the next screen shown to the user by manipulating the%_REQUEST
hash. For example, it's usual to set theid
parameter after creating new object:sub do_create_users_for_admin { sql_do ("INSERT INTO ... "); $_REQUEST {id} = sql_last_insert_id (); }
The client window will be rediredted to "/?type=users&id=1&sid=...".
- get_item_of_${type}_for_${role}
-
This sub must fetch the info for the screen of type
$type
having the obgect id$_REQUEST {id}
and the role${role}
. Usually it's a reference to a hash, may be nested. - select_${type}_for_${role}
-
This sub must fetch the info for the screen of type
$type
and the role${role}
. Usually it's a reference to a list of references to hashes, may be nested. - draw_item_of_${type}_for_${role}
-
This sub must render the screen of type
$type
having the obgect id$_REQUEST {id}
and the role${role}
as HTML. The info fetched withget_item_of_${type}_for_${role}
is passed as its 1st parameter. - draw_${type}_for_${role}
-
This sub must render the screen of type
$type
sand the role${role}
as HTML. The info fetched withselect_${type}_for_${role}
is passed as its 1st parameter.
HTTP REQUEST HANDLING
SESSION CHECKING
First of all, the handler checks for the sid
param and, if the session is alive, it sets the $USER
variable, otherwise, redirects the client to the logon screen.
EDITING REQUEST
If the action
CGI parameter is set, then the sub named validate_{$action}_${type}_for_${role}
is invoked. If if returns a non-empty error message, it's logged and presented with a js popup window. Otherwise the sub named do_{$action}_${type}_for_${role}
is invoked, then the client is redirected to the new URL composed from all %_REQUEST
key-value pairs except action
and those which names start with an '_'
.
In any case, the HTTP response has status 200 (OK) and contains a tiny HTML document consisting of a singular body
tag with a non-empty onLoad
event handler. When an error occurs, this handler displays the message in a js popup window. Otherwise the onLoad
handler opens the new URL in the top browser window.
Every conventional HTML page generated by Zanas Apache handler has a zero sized internal frame called invisible
. In order to improve the GUI usability, every anchor with non-empty action
parameter value in its href and every form with a non-empty value for action
input must use invisible
as the target:
<a href="/type=folder&action=create" target="invisible">[New Folder]</a>
<form action="/" target="invisible">
...
</form>
Standard Zanas HTML rendering API does this automatically.
OBJECT BROWSING REQUEST
If the action
CGI parameter is unset and id
CGI parameter is set, then the HTML resuls from the superposition of draw_item_of_${type}_for_${role}
and get_item_of_${type}_for_${role}
callbacks.
SELECTION BROWSING REQUEST
If both action
and id
CGI parameters are unset, then the HTML resuls from the superposition of draw_${type}_for_${role}
and select_${type}_for_${role}
callbacks.
MODULES STRUCTURE
Zanas modules don't have a package
directive. All the stuff is loaded in one package.
Callback subs must be placed in strictly named .pm files. Suppose that you've chosen $applib
as your application library root and have placed it in your @INC
array. Then, create $applib/Content
and $applib/Presentation
directories.
Now, all content callbacks (validate_{$action}_${type}_for_${role}
, do_{$action}_${type}_for_${role}
, get_item_of_${type}_for_${role}
and select_${type}_for_${role}
) must be defined in $applib/Content/${type}.pm
and presentation callbacks (draw_item_of_${type}_for_${role}
and draw_${type}_for_${role}
) in $applib/Presentation/${type}.pm
.
$applib
Content
roles.pm
users.pm
Presentation
roles.pm
users.pm
SEE ALSO
Zanas::Presentation
AUTHOR
Dmitry Ovsyanko <do@zanas.ru> Pavel Kudryavtzev <pashka@zanas.ru>
1;