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 and p == 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 the id 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 with get_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 with select_${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;