NAME

CGI::Panel - Create stateful event-driven web applications from simple panel objects

SYNOPSIS

A very simple working application consisting of a driver cgi and two panel classes...

In simpleapp.cgi:

use SimpleApp;
my $simple_app = obtain SimpleApp;
$simple_app->cycle();

In SimpleApp.pm:

package SimpleApp;

use base qw(CGI::Panel);
use Basket;

sub init {
    my ($self) = @_;
    $self->add_panel('basket1', new Basket); # Add a sub-panel
    $self->add_panel('basket2', new Basket); # Add a sub-panel
    $self->add_panel('basket3', new Basket); # Add a sub-panel
    $self->{count} = 1;   # Initialise some persistent data
}

sub _event_add {    # Respond to the button click event below
    my ($self, $event) = @_;
    
    $self->{count}++;  # Change the persistent data
}

sub display {
    my ($self) = @_;

    return
	'This is a very simple app.<p>' .
	# Display the persistent data...
	"My current count is $self->{count}<p>" .
	# Display the sub-panels...
	"<table><tr>" .
	"<td>" . $self->panel('basket1')->display . "</td>" .
	"<td>" . $self->panel('basket2')->display . "</td>" .
	"<td>" . $self->panel('basket3')->display . "</td>" .
    "</tr></table>" .
	# Display a button that will generate an event...
	$self->event_button(label => 'Add 1', name => 'add');
}

1;

In Basket.pm:

    package Basket;

    use base qw(CGI::Panel);
    
    sub init {
        my ($self) = @_;
    
        $self->{contents} = [];
    }
    
    sub _event_add {  # Respond to the button event in 'display'
        my ($self, $event) = @_;

        # Get panel's localised parameters    
	# (Many instances of this panel each get
	# their own local parameters)
        my %local_params = $self->local_params;
    
        push @{$self->{contents}}, $local_params{item_name};
    }
    
    sub display {
        my ($self) = @_;
    
        return
          '<table bgcolor="#CCCCFF">' .
          join('', (map { "<tr><td>$_</td></tr>" } @{$self->{contents}})) .
          '<tr>' .
	          # Localised text field
    	  '<td>' . $self->local_textfield({name => 'item_name', size => 10}) . '</td>' .
                  # Button that will generate an event (handled by _event_add above)
    	  '<td>' . $self->event_button(label => 'Add', name => 'add') . '</td>' .
          '</tr>' .
          '</table>';
    };
    
    1;

This example is included with the module. It's in the 'demo' directory and can be seen in action at http://www.cyberdesignfactory.com/public-cgi-bin/simpleapp.cgi

DESCRIPTION

CGI::Panel allows applications to be built out of simple object-based components. It'll handle the state of your data and objects so you can write a web application just like a desktop app. You can forget about the http requests and responses, whether we're getting or posting, and all that stuff because that is all handled for you leaving to you interact with a simple API.

An application is constructed from a set of 'panels', each of which can contain other panels. The panels are managed behind the scenes as persistent objects. See the sample applications for examples of how complex object-based applications can be built from simple encapsulated components. To try the demo app, copy the contents of the 'demo' directory to a cgi-bin directory.

CGI::Panel allows you to design the logic of your application in an event-driven manner. That is, you set up your application the way you want it, with special buttons and links that trigger 'events'. The application then sits back and when an event is triggered, the code associated with that event is run. The code that responds to an event goes in the same class as the code that generates the event button or link, making the code more readable and maintainable. If the event code changes the state of any of the panels, the panels will then stay in the new state, until their state is changed again.

Each panel is encapsulated not only in terms of the code, but in terms of the form data that is passed through. For example a panel class can be defined which has a textfield called 'name'. Three instances of this panel can then exist simultaneously and each will get the correct value of the 'name' parameter when they read their parameters (see the 'local_params' method).

Please let me know by email if you're using the module and would like to be informed when there's an update.

USAGE

See 'SYNOPSIS'

BUGS

SUPPORT

AUTHOR

Robert J. Symes
CPAN ID: RSYMES
rob@robsymes.com

COPYRIGHT

Copyright (c) 2002 Robert J. Symes. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file included with this module.

SEE ALSO

perl(1).

PUBLIC METHODS

Each public function/method is described here. These are how you should interact with this module.

new

Creates a new panel object

Use:

my $panel = new Panel;

init

Initialises a panel object. This should be used to add panels to the current panel. We provide a default method here which can be overridden.

Example:

sub init {
    my ($self) = @_;

    $self->add_panel('first_panel',  App::Panel::First);
    $self->add_panel('second_panel', App::Panel::Second);
}

parent

Get or set the parent of the panel object.

Examples:

my $parent = $self->parent;
$self->parent($other_panel);

get_session_id

Gets the session id for the application

Note: It's essential that all panels are added using the proper add_panel routine for this routine to work correctly.

Example:

my $id = $self->get_session_id;

panel

Retrieves a sub-panel by name

Example:

my $first_panel = $self->panel('first_panel');

get_panels

Retrieves the set of panels as a hash

Example:

my %panels = $self->get_panels;

get_id

Gets the id of the panel. If one is not currently stored, we generate a new one with help from the main panel. This method can be overridden if you want to give a unique name to a panel.

Examples:

sub get_id { 'unique_name' }

or

my $id = $self->get_id;

and later...

$self->get_panel_by_id('unique_name');

or

$self->get_panel_by_id($id);

See documentation of get_panel_by_id for more details. (Of course, you can also just use this get_id to get the auto-generated id and use that later in get_panel_by_id.)

main_panel

Get the main panel (by recursing up the panel tree) Eventually this will reach a panel without a parent, which we will assume to be the main panel.

Example:

my $main_panel = $self->main_panel;

add_panel

Adds a panel to the current panel in a way that maintains referential integrity, ie the child panel's parent value will be set to the current panel. All panels should be added to their parents using this routine to keep referential integrity and allow certain other mechanisms to work. Specify the name to refer to the panel by and the panel object.

Example:

$self->add_panel('first_panel', new App::Panel::First);

remove_panels

Remove all the panels from the current panel.

Example:

$self->remove_panels;

local_params

Get the parameter list for the current panel. This fetches the parameter list and returns the parameters that are relevant to the current panel. This allows each panel to be written in isolation. Two panels may have input controls (textboxes etc) with the same name and they can each retrieve the value of that input from their %local_params hash.

eg

my %local_params = $self->local_params;
my $name = $local_params{name};

event_button

Display a button which when pressed re-cycles the application and generates an event to be handled by the next incarnation of the application. The name of the routine that will be called will have _event_ prepended. This is partly for aesthesic reasons but mainly for security, to stop a wily hacker from calling any routine by changing what is passed through the browser. We'll probably be encrypting what is passed through in a later version.

Input:
  label:       Caption to display on button
  name:        Name of the event
  routine:     Name of the event routine to call
               (defaults to name value if not specified)
               ('_event_' is prepended to the routine name)
  other_tags:  Other tags for the html item

For example:

$shop->event_button(
    label      => 'Add Item',
    name       => 'add',
    routine    => 'add',
    other_tags => {
        class => 'myclass'
    }
);

Display a link (which can be an image link) which when pressed re-cycles the application and generates an event to be handled by the next incarnation of the application.

Input:
  label:       Caption to display on link
   * OR *
  img:         Image to display as link

  name:        Name of the event
  routine:     Name of the event routine to call
               (defaults to name value if not specified)
               ('_event_' is prepended to the routine name)
  other_tags:  Other tags for the html item
  img_tags:    Other tags for the image (if the link is an image)

For example:

$shop->event_link(
    label => 'Add Item',
    name  => 'add',
    other_tags => {
        width => 20
    }
);

CGI input functions

The CGI input functions are available here with local_ prepended so the name can be made panel-specific, and they can be called as a method. The same effect can be achieved by using the get_localised_name function for the name of the parameter.

Example:

$self->local_textfield({name => 'testinput', size => 40})

is equivalent to:

my $cgi = new CGI;
$cgi->textfield({name => $self->get_localised_name('testinput'), size => 40})

Using these methods means that the panel will have exclusive access to the named input parameter. So to obtain the value of the input parameter above, we would write the following:

my %local_params = $self->local_params;
my $test_input_value = $local_params{'testinput'};

Note that with this technique, several panels could have input controls with the same name and they will each receive the correct value. This is especially useful for sets of panels of the same class.

get_localised_name

Return a name that has the panel id encoded into it. This is used by the local_... functions and can be used to build a custom html input control that will deliver its value when the panel's local_params method is called.

Example:

$output .= $cgi->textfield({name => $self->get_localised_name('sometext')});

The equivalent could be done by calling:

$output .= $self->local_textfield({name => 'sometext'});

local_textfield

Generate a localised textfield

Example:

$output .= $self->local_textfield({name => 'sometext'});

MAIN PANEL METHODS

These methods provide extra functionality useful for the main panel of an application. Apache::Session is used to handle session information. An application built using the CGI::Panel framework should typically have one main panel and a hierarchy of other panels, all of which inherit from CGI::Panel.

obtain

Obtains the master panel object

This will either restore the current master panel session or create a new one

Use:

my $shop = obtain Shop;

cycle

Performs a complete cycle of the application

Takes all the actions that are required for a complete cycle of the application, including processing events and form data and displaying the updated screen. Also manages persistence for the panel hierarchy.

Use:

$shop->cycle();

save

Saves an object to persistent storage indexed by session id. You don't normally need to explicitly call this in your application, as it's called during the 'cycle' method.

Use:

$self->save;

get_panel_by_id

Look up the panel in our list and return it. Note that this is different to the 'panel' routine in CGI::Panel, which gets a sub-panel of the current panel by name. All the panels in an application will be registered with the main panel which stores them in a special hash with an automatically generated key. This routine gets any panel in the application based on the key supplied.

Use:

my $panel_id = $main_panel->get_panel_by_id(3);

OTHER METHODS

The following methods are used behind the scenes, usually from the 'cycle' method above. They will generally be sufficient as they are but can be overridden if necessary for greater flexibility.

register_panel

Accept a panel object and 'register' it - ie store a reference to it in a special list. Return the id (hash key) to the caller.

Use:

my $id = $main_panel->register($panel);

screen_main

Display main screen for the master panel. This is called automatically by the 'cycle' routine. Other screen methods can be defined if necessary, however judicious use of panels should avoid the need for this.

handle_event

Handle a button event by passing the event information to the appropriate event routine of the correct panel. Currently this is always the panel that generates the event.

Handle a link event by passing the event information to the appropriate event routine of the correct panel. Currently this is always the panel that generates the event.

interpret_messages

Read the request information using the CGI module and present this data in a more structured way. In particular this detects events and decodes the information associated with them.

session_directory

This method returns the name of the directory that is used to store the session files. It's currently set to '/tmp'. Override this method to return a different directory if desired.

lock_directory

This method returns the name of the directory that is used to store the lock files. It's currently set to '/tmp'. Override this method to return a different directory if desired.