ExSite Kernel Development

The ExSite kernel consists of the libraries that perform the core services of ExSite. These services include:

If the generic ExSite distribution satisfies your requirements, there may be no need to work directly with kernel code at all. In some cases, however, it may be desirable to tweak or tune the kernel in small ways. ExSite includes a lot of configuration parameters that allow you to easily select between predefined kernel options. ExSite also provides a number of easy ways to register custom handlers to the kernel, to customize kernel behaviours in certain cases. ExSite also provides some localization modules, allowing you to add to the kernel's features, while still keeping your customizations physically separate from the kernel files.

In extreme cases, you may want to modify the kernel code outright. This gives you a lot of power to customize the system to your requirements, but it also makes it more difficult to upgrade your distribution when new features are released.

Configuration

Kernel configuration is done through the file exsite.conf. This file contains configuration parameters that are arranged in groups and sub-groups, for example:

	group.subgroup.parameter = value

Values for all kernel configuration parameters are hard-coded in the kernel file ExSite::Config.pm. Only values that differ from the hard-coded defaults need be included in exsite.conf.

In a simple configuration, exsite.conf will include a few lines describing/identifying your website, plus a few lines describing your database(s) to connect to. Everything else is optional.

As an introduction to ExSite kernel configuration, here are a few examples of configurations that can be make directly from the exsite.conf file. A full description of configuration parameters is available in the kernel docs.

Example Configuration Parameters

DES-encrypt passwords stored in the login tables:

	auth.password_storage_method = des

Display form inputs in a vertical list with labels above input fields:

	form.style = list

Highlight required fields in auto-generated forms using an asterisk:

	form.reqd_field_style = star

When displaying images taken from the database, show them as links to an image, instead of showing the image itself:

	show_images = 0

Localization

ExSite looks for and attempts to load the following Perl modules when it starts:

	myConfig.pm
	Local.pm

In the generic ExSite distribution, these files are dummies; they may contain a few subroutines, but no meaningful code. These routines can be expanded/replaced in any local ExSite installation.

myConfig.pm

The myConfig module provides a simple mechanism to customize the configuration of your ExSite kernel when it starts up. It contains a handful of dummy routines that will be called by the kernel at particular times. If you wish to provide system logic for these instances, you can insert code into these routines; otherwise leave them blank and ExSite will fall back on its default logic.

my_exsite_init()
This routine is executed after ExSite has read its configuration files and performed any auto-configuration. You can do any additional setup or configuration required by your site here.

my_exsite_close()
Similarly, this routine is called when ExSite finishes up. Any clean-up that needs to be done should be taken care of here.

my_handlers()
This routine is called whenever ExSite creates a new kernel-level object (such as a database access object). You can register special kernel handlers to assume control over special aspects of the system logic, by inserting the handler registration calls here. An example of registering a handler:
	sub my_handlers {
	    my $this = shift;
	    # register some special validation logic
	    $this->handler("validate",\&my_validation_routine);
	}
See the Handlers section, below, for more info.

my_page_header(), my_page_footer()
These routines construct the HTML that preceeds and follows the core content of an ExSite system-generated page. (This is different from the pages comprising your website, which use a more flexible templating scheme.) You may optionally customize the appearance of the ExSite system pages by inserting your own custom header and footer HTML code into these routines.

Local.pm

Local.pm contains no predefined routines. It is simply a repository for any shareable code required by your site. Any modules or routines that need this code can simply "use Local.pm" to get access to these routines. Note that myConfig.pm automatically uses Local.pm.

Even if your site has a lot of custom code, it does not necessarily need to go into Local.pm. Self-contained web applications and modules will tend to reside in their own files elsewhere. Local.pm is mostly useful if you have some utility routines that are shared by:

In the event that there is no obvious or convenient place to put the shared utility code, Local.pm can be used.

Handlers

The ExSite kernel makes a variety of assumptions about business logic, database handling, reporting, and so on, for the sake of simplicity. In many cases, however, the default ExSite logic may not be adequate for your particular case. You have several options:

Usually the last option is easiest, and it also prevents unnecessary modification of system code, which simplifies system upgrades and maintenance in future.

Handlers are registered with a simple call:

	$obj->handler("NAME",\&handler_routine);

where $obj is the ExSite object you are registering the handler with, "NAME" is the name you are registering the handler under, and \&handler_routine is a reference to your custom handler routine (which must be visible in the scope of the above call).

Handlers are invoked by name at specific points in the kernel, wherever it is possible that a particular web application may want to override or add to the default logic. At these locations, ExSite checks the handler registry to see if any handlers have been registered under the name of that location. If so, the handler is executed with a set of parameters that are location-specific. Typically, ExSite checks the handler output; if the output is undefined, ExSite ignores it and proceeds with the default system logic. If there is output from the handler, the default system logic will be skipped, and the handler output will be used as its replacement.

This is best illustrated with an example. The kernel method that displays a datum taken from an ExSite-managed database is ExSite::Report::show_data(). It accepts up to 4 parameters: the table name, the column name, the data value, and optionally the record id. ExSite determines how to display the data value based on the datatype of this column and other factors. If you needed to override this logic in particular cases, you could create a handler, for instance:

sub my_show_data {
    my ($this,$table,$column,$data,$id) = @_;
    
    # special display logic for internet users
    if ($ENV{REMOTE_ADDR} !~ /^192\.168/) {
	# user is not on our LAN - 
	# block the display of all columns from "secure_data" table 
	if ($table eq "secure_data") {
	    return "n/a";
        }
    }

    # in all other cases, return undef to use default kernel logic
    return undef;
}

In this example, we override the ExSite::Report::show_data() logic in the case where the system is showing data from one particular table to a user who is not on our LAN. In this case, the system always returns "n/a" as the data value. Otherwise the handler returns undefined, which tells ExSite that the handler produced no result and ExSite should proceed with its normal logic to display the requested data.

The above handler would be registered using a call like this:

$db->handler( "show_data", \&my_show_data );

This call can be placed somewhere appropriate, eg. in myConfig::my_handlers().

Multiple instances of a single handler may be registered. This may occur if two or more widely separated components of your website both require special handling in the same unit of business logic (eg. validation of form data). In this case, the handlers are executed in the same order that they were registered in. The first handler to return a defined result will take precedence. If none of the handlers return a defined result, ExSite will use the default kernel logic.

List of Handlers

This is a partial list of handlers recognized by the ExSite kernel. If you need to use one of these handlers, it is recommended that you examine the actual invocation of the handler in the module in question. This will show you the required parameters, and the expected return values that you need to conform to.

Module	Name			Description

DB	identify		determine who the user claims to be
DB	lookup			look up the user's claimed identity
DB	authenticate		authenticate the user's identity claim
DB	authorize		approve a user's database operation
DB	user_owns		determine if the user owns a record
DB	group_owns		determine if the user's group owns a record
DB	find_owner		determine the owner of a record
Form	form_input		preprocess form data
Form	cancel_form		process a form cancel button press
Form	search			process a database search command
Form	validate		validate form inputs
Form	form_output		post_process an automatically-generated form
Form	input			generate an <input> tag
Form	input_column		generate an <input> tag for a table/column
Form	input_exsite		generate an <input> tag for an exsite datatype
Form	select_foreign_key	choose the set of foreign keys to select from
Form	select_foreign_key_label	determine how to display a foreign key
Report	show_data		display a datum from the database
Report	report			build a generic database report
Report	report_table_header	build the top-of-report HTML
Report	report_*_format		build an individual row of a report
SQL	pre_select		pre-process a database select request
SQL	post_select		post-process a database select request
SQL	pre_insert		pre-process a database insert request
SQL	post_insert		post-process a database insert request
SQL	pre_update		pre-process a database update request
SQL	post_update		post-process a database update request
SQL	pre_delete		pre-process a database delete request
SQL	post_delete		post-process a database delete request

Handler Registration

Handlers can be registered system-wide my placing them into myConfig::my_handlers(). This causes the handlers to be registered into every ExSite kernel object that is created, which may result in a slight efficiency penalty if your handlers are only relevant to very particular situations.

In more particular situations, the handler can be explicitly registered into particular kernel objects, as needed. For instance, if a handler only needs to run in the context of a particular module or CGI program, then it can be registered in that program/module only. For example:

	# in myprog.cgi
	my $db = new ExSite::Form;     # create Form object
	$db->handler("NAME",\&mycode); # add a handler to this object

	# ...
	
	sub mycode {
	    # handler code here
	}	

Database Drivers

ExSite uses a simple relational database API that is extensible to a wide variety of database engines. ExSite assumes that its data is organized into named tables, which contain named rows (or records) that are divided into named columns (or fields). Typically, tables and columns have text names; rows normally have numeric names (or IDs), but they can have text names alternatively. Row names are usually called "primary keys", in keeping with the conventions of database nomenclature. ExSite prefers that its data have unique primary keys, so that rows are uniquely addressible. In most cases this is achived by using numeric key that references the row number. Since the primary key is treated like another column in the data, primary keys have column names, like any other column.

By following this convention, any single piece of data in an ExSite database is uniquely addressible by its table, record id, and column.

ExSite database drivers support the following API methods:

	get_tables() - returns an array of known table names
	get_columns() - returns an array of column names in a table
	get_key() - returns the primary key column name for a table
	select() - returns an array of rows selected from a table
	insert() - adds a new row to a table
	update() - edits/modifies a set of rows in a table
	delete() - removes a set of rows from a table

This data structure and API are generic enough that it is straightforward to write a driver for nearly any kind of low-level database. ExSite comes with Text (ie. tab-delimited text file) and MySQL drivers included, and these can be used as models for developing other drivers if they are needed.

To take full advantage of the ExSite kernel's database management code, you will also need a database map, which is a meta-database describing the datatypes, web behaviours, and relational structure of your actual database. The database map is typically a tab-delimited text database (which is addressed using ExSite's Text database driver, and queried using the DBmap kernel module). Database maps are fully described in the DBmap kernel documentation.

With a driver and a database map, your database has access to the full suite of ExSite's database tools, including:

Secondary Databases

ExSite's primary database is used for general content management. If your website or web applications require their own database tables, you have the option of including them in the primary database, or running with secondary databases, as needed. Running from a single database is more efficient, but in some cases you might feel it is less secure.

Secondary databases can be designed and built as ExSite databases, using the above API, but there is no requirement for that. If your web application uses an existing database that is quite different in design, then it may be simpler just to write your own custom database interface to it which is used only by your module.

Kernel Architecture

TBD