The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

OpenThought - Web Application Engine

SYNOPSIS

 use OpenThought();

 my $o = OpenThought->new( $OP );

 my $field_data;
 $field_data->{'myTextBox'}    = "Text Box Data";
 $field_data->{'myCheckbox'}   = "true";
 $field_data->{'myRadioBtn'}   = "RadioBtnValue";
 $field_data->{'mySelectList'} = [
                                    [ "text1", "value1" ],
                                    [ "text2", "value2" ],
                                    [ "text3", "value3" ],
                                 ];

 my $html_data;
 $html_data->{'id_tagname'} = "New HTML Code";

 print $o->serialize({
            fields     => $field_data,
            html       => $html_data,
            focus      => "myTextBox",
            javascript => $javascript_code
 });

DESCRIPTION

OpenThought is a powerful and flexible web application environment. OpenThought applications are different from other web applications in that all communication between the browser and the server is performed in the background. This gives a browser the ability to receive data from the server without ever reloading the currently loaded document. Data received can be displayed automatically on the existing page, can access JavaScript functions and variables, and can load new pages. Additionally, OpenThought completely manages all of your session data for you. These features give the look and feel of a full-blown application instead of just an ordinary Web page.

OpenThought is extended with OpenPlugin , also described briefly in this documentation.

FUNCTIONS

new
 $o = OpenThought->new( $OP, {
                Prefix          => "/path/which/overrides/config",
                OpenThoughtRoot => "/path/which/overrides/config",
             });

Creates a new OpenThought object.

Parameters
$OP

An OpenPlugin object.

Prefix (optional)

The directory prefix OpenThought Data Files were installed under.

The value for this is typically set in the OpenThought-httpd.conf file. However, if you wish to override it, or you don't have the ability to edit that file (ie, you don't have root on the webserver), you can set Prefix here.

OpenThoughtRoot (optional)

The full path to the directory under which all OpenThought Applications will be located. Please include a trailing slash.

The value for this is typically set in the OpenThought-httpd.conf file. However, if you wish to override it, or you don't have the ability to edit that file (ie, you don't have root on the webserver), you can set OpenThoughtRoot here.

Return Value
$o

OpenThought object.

settings
 $string = $o->settings({  autoclear         => boolean,
                           maxselectboxwidth => size,
                           fetchstart        => string,
                           fetchdisplay      => string,
                           fetchfinish       => string,
                           runmodeparam      => string,
                           order             => [ "fetchstart", "autoclear" ]
                        });

Alter settings in the currently running OpenThought application. These settings will persist, until otherwise altered. To temporarily change settings, these same options can be used in the serialize function.

Parameters
autoclear

The default behaviour for select lists is to clear themselves before data is put into them. Setting autoclear to a false value is one method of altering that behaviour. If autoclear is false, the contents of a select list are preserved, and any new data is appended to the select list. Any number less then 1 and 'false' are considered false values, everything else is true.

maxselectboxwidth

Aside from Netscape 4, all browsers which receive text into a select box resize that select box to the width of the longest entry. Select box resizing is neat, but sometimes it ends up being much to big, and can adversly affect other parts of your visual layout. This option allows you to modify the size of text going into a SelectBox, so the browser doesn't make the select box too big. Text going into a select box will be truncated if it is longer then the max width set here. This can be any number, or 0 to not constrain the size.

nullreply

If the server sends us a packet which contains no data at all, give this message to the user in a popup box.

runmodeparam

The name of the parameter which holds the run mode. The JavaScript in the browser uses this when you send data the the server -- it figures out which run mode you are trying to run, and sets the appropriate parameters, so OpenPlugin::Application can find it.

order

As parameters are serialized and executed in a particular order, this parameter allows you to override the default and specify what this order will be. This is a reference to an array, containing the proper order for all the items in the call. Although this is more useful in the serialize function, it still could be beneficial to alter the order of settings. The current order these are executed in are: autoclear, maxselectboxwidth, runmodeparam, fetchstart, fetchdisplay, and fetchfinish.

Return value
$string

A string, which contains the settings to be given to the browser. Often this is just given directly to the browser by your application, although you can modify it first if you desire.

serialize
 $string = $o->serialize({  fields     => $field_data,
                            html       => $html_data,
                            focus      => $field_name,
                            javascript => $javascript_code,
                            url        => $content_url,
                            setting    => $value,
                            order      => [ "javascript", "fields" ],
                        });

Serialize data that you wish to give to the browser.

Parameters
fields

A hash reference containing keys which map to HTML form element names in the HTML document. The value of each hash key will be displayed in the corresponding field.

For radio buttons, the value for the hash key should be the value of the radio button element in the HTML. The radio button with the name mapping to the hash key, and value mapping to that particular hash value, will be the one that gets selected.

Select List elements are special cases. Select Lists should have a hash key with a reference to an array of arrays.

There are examples of these below.

html

A hash reference containing keys which map to HTML id attributes, such as those found in div tags. The value of each hash key will be displayed within the corresponding id attribute.

focus

A string containing the name of the field within the HTML code which you wish to focus.

javascript

A string containing the JavaScript code you wish to run. This will be run by the browser, within your applications namespace. You do not need to add script tags.

url

The url parameter allows you to switch between html documents. All your base files that are loaded remain the same -- meaning your session and such are preserved -- what changes is the user interface, when using this option. It first expires the cache associated with the form elements in the existing page, and then loads the new page you requested.

setting

This is the same as calling the settings function, except that when used here, the setting is only changed for this one call, and then it is restored to it's previous value. The keyword to use for this option is not the literal word 'setting', but any of the settings listed under the settings function.

order

As parameters are serialized and executed in a particular order, this parameter allows you to override the default and specify what this order will be. This is a reference to an array, containing the proper order for all the items in the call. The default order is: settings (see settings function), followed by fields, javascript, url, and focus.

Return value
$string

A string, which contains the serialized data to be given to the browser. Often this is just given directly to the browser by your application, although you can modify it first if you desire.

deserialize
 $hashref = $o->deserialize( OpenThought )

Retrieve the parameters sent to us during a typical OpenThought request.

Parameters
OpenThought

The parameter, passed to us from the browser, named 'OpenThought'. This can be retrieved using $self->OP->param->get_incoming( 'OpenThought' ).

Return value

Deserialize returns a hash reference with the following keys:

fields

A hash reference containing keys and values which map to the HTML form field names and values we were sent from the browser.

expr

A hash reference containing keys/value pairs as sent to us by the browser. These are expressions which the application developer wanted sent to the server upon a particular event.

session_id

A string containing the session id for your application.

$run_mode

A string containing the current run mode. The key $run_mode is the run_mode_param, as set in the config file, or overridden with the settings() or serialize() functions.

get_application_base
 $string = $o->get_application_base( [ $url ] )

Retrieve the base files required for a browser to build an OpenThought Application

Parameters
$url (optional)

If the url where the OpenThought HTML Content can be found is different from the one the browser just requested, include it here. Otherwise, it defaults to using the current url.

Return value
$string

Returns as a string the html base code that is required in order for your application to run. Often this is just given directly to the browser by your application, although you can modify it first if you desire.

INTERACTING WITH THE BROWSER

Sending Data to the Browser

The following methods show you how you can send data from the server to the browser.

Just a Hashref

You only need a reference to a hash to send data to the browser. If the hashref containing all of our field data is called $field_data, then all we need to do in our code is:

 # Send the outgoing HTTP Header
 $OP->httpheader->send_outgoing();

 # Populate the form fields and html with the data within our hashref
 print $o->serialize({ fields => $field_data, html => $html_data });

Populating Text, Password, and Textarea HTML Elements

 $field_data->{'fieldname'} = "data";

Populating and Selecting Select List HTML Elements

 $field_data->{'selectbox_name'} = [
                                        [ "Example 1", "value_one"   ],
                                        [ "Example 2", "value_two"   ],
                                        [ "Example 3", "value_three" ],
                                   ];

This will set the text of a select box to the left column above, and the value of that same select box to the right column.

In the case that you don't have two columns worth of data you wish to use, you can also do:

 $field_data->{'selectbox_name'} = [
                                     [ "Example 1"  ],
                                     [ "Example 2"  ],
                                     [ "Example 3"  ],
                                   ];

This makes both the text and value of the selectbox identical, and requires sending less data to the browser (which, of course, saves bandwidth).

Also, if autoclear is set to false (not the default), you can use the following to manually clear the contents of a select list:

 $field->{'selectbox_name'} = [ "" ];

To select (highlight) an item in an existing list of elements, you can send a select list a single string like so:

 $field->{'selectbox_name'} = "optionvalue";

The option 'optionvalue' represents the value assigned to that particular option in the select list.

Selecting Checkbox HTML Elements

 $field_data->{'checkbox_name'} = "value";

To uncheck a checkbox, set value to any of:

    • false | False | FALSE

    • unchecked

    • any number less then 1

Setting the value to anything other then the above will cause the checkbox to be checked.

Selecting Radio Button HTML Elements

 $field_data->{'radiobtn_name'} = "radiobtn_value";

radiobtn_value is the value in the "value=" tag of the radio button.

Radio buttons can only be selected, they cannot be unselected.

Updating Existing HTML Code

 $html_data->{'id_tagname'} = "New HTML Code";

This inserts the text "New HTML Code" inside the tag with the id attribute labeled 'id_tagname'. This replaces and text or code that may have originally existed within that tag.

Unlike updating form data, updating HTML currently only works in browsers which support the 'innerHTML' method. That includes IE 5.x, Netscape 6.x, and Mozilla.

Focusing an Element

You can give the focus to any form element within the browser simply by saying:

 $o->serialize({ focus => "fieldname" });

Running JavaScript

You can easily send JavaScript to the browser, allowing you to call JavaScript functions, access JavaScript variables, and even create new functions -- all from the server.

The following calls the JavaScript 'alert' function:

 $o->serialize({ javascript => 'alert("Hello!");' });

The next example calls the hypothetical javascript function 'myfunction', using param1 and param2 as arguments to that function:

 $o->serialize({ javascript => 'myfunction(param1, param2);' });

You can send any JavaScript you want, but make sure it's properly formated code. OpenThought does not validate whether or not your JavaScript syntax is correct, the browser will be your judge! If something isn't working, pull up your browsers JavaScript console, it may provide you with some insight as to what isn't working properly. You do not need to include script tags.

Loading a New Page

There are plenty of cases where it may be desirable load a new page, and to not keep all your data on one particular page. Loading a new page is quite simple, and can be initiated from the server, or from the browser.

Here is an example of how you might tell the browser, from the server, to load a new URL:

 $o->serialize({ url => '/OpenThought/newurl.pl' });

This function will have the browser call the perl script 'newurl.pl'. It's then up to newurl.pl to deliver some sort of content back to the browser. Although you are loading a new page, the session is preserved across this call.

You may also load a new page from the browser when a user clicks a button or link. See the 'FetchHtml()' function in 'Sending Data to the Server' below.

Using DBI

You can send the results of a database search directly to the browser, and have the data from the results put into the respective fields. You only need one thing in order for this to work -- the field names in your database just need to match your field names in the HTML. For example:

 my $sql = "SELECT name, address, phone, age, married " .
           "FROM sometable WHERE name="Tim Toady";

 my $sth = $dbh->prepare($sql);
 $sth->execute;

 $field_data = $sth->fetchrow_hashref;

In this case, lets say we have 'name', 'address', 'phone', and 'age' as text fields in our HTML, and 'married' is a checkbox. As soon as we send $field_data to the browser, these fields will all be filled in with the appropriate data.

This also works for select lists:

 my $sql = "SELECT name, ssn FROM sometable";

 my $sth = $dbh->prepare($sql);
 $sth->execute;

 $field_data->{people} = $sth->fetchall_arrayref;

This selects the name and social security number from everyone in the table, and will allow us to use it to populate a select list named 'people'.

Sending Data to the Server

You can send data from the browser to the server anytime you want by simply generating an event. Events are often generated by clicking buttons or links. JavaScript functions like onMouseOver, onClick, onChange, etc.. they can all allow you to cash in on an event, and you can take advantage of them to send data to the server at that time.

The two JavaScript functions available to you for communicating with the server are CallUrl() and FetchHtml(). Their usage and parameters is identical, but they perform very different functions. CallUrl() sends data in the background to the server, and the server can respond in the background with it's own packet. This function does not make the page reload. The function FetchHtml, however, does reload the page. It's sole purpose is to fetch HTML content for your content screen, and replaces your existing content with the content it is given. This is a fancy way of saying it just loads a new page.

The following are some examples of how you might use these two functions. In any of the following situations, the two functions are interchangable. It all just depends on what you want to happen.

Button Events

 <input type=button name=search value='Click me!'
        onClick="parent.CallUrl('/OpenThought/servercode.pl',
                                'field1', 'field2', 'foo=bar');

Upon clicking this button, it will send the current contents of the fields named 'field1' and 'field2' to /OpenThought/servercode.pl. It will also send the expression "foo=bar". When this gets to the server, 'foo' will be a hash key, 'bar' will be set to the value of that key.

Be careful using submit and image buttons. You don't want your form to actually perform a "submit", which causes the page to refresh. You are merely looking to "catch" the submit event, and perform an action when that submit even is generated. If you wish to use a submit or image button, you should define your form like this:

 <form onSubmit="return false">

Now your browser can use submit and image buttons to send data to the server without refreshing.

Using Links

The following example shows you how you can use a typical HTML link to send data to the server without causing the page to refresh:

 <a href="javascript:parent.CallUrl('/OpenThought/servercode.pl',
                                    'run_mode=forgot_password')">Click me!</a>

Select List Events

 <select name=mySelectList size=10
         onChange="parent.CallUrl('/OpenThought/servercode.pl',
                                  'mySelectList')>

Whenever a new item in this select list is highlighted, it will send it's value to /OpenThought/servercode.pl.

EXTENDED FUNCTIONALITY: OPENPLUGIN

Instead of building all OpenThought's functionality into the core OpenThought modules, non-core functionality was split off into a reusable component called OpenPlugin. OpenPlugin is a plugin manager which may be used in any web application, including OpenThought. The list of plugins if offers is currently:

OpenPlugin Usage

Here you will see methods of how to use OpenPlugin within OpenThought. For more information on OpenPlugin, please see the OpenPlugin documentation included with the OpenPlugin distribution.

Application

This is a powerful component which is a subclass of CGI::Application. It, in the authors words, will "make it easier to create sophisticated, reusable web-based applications".

While using OpenPlugin::Application isn't necessary, it's highly (very highly!) recommended.

Authenticate

Authenticate a user:

 $auth = $OP->authenticate->authenticate({ username => "some_username",
                                           password => "some_password",
                                        });

The variable $auth will be true if the user is successfully authenticated, false otherwise.

Cache

If you wish to use a cache:

 $OP->cache->save( \%cache_data, {
                         id      => "Name for this cache entry",
                         expires => "+3h",
                 });

And then later, to retrieve the cache:

 my $cache = $OP->cache->fetch( "Name for this cache entry" );

$cache will be undef if there is no valid cache data for the id being given.

A cookie can be sent to the browser with the following:

 $OP->cookie->set_outgoing({  name    => 'testcookie',
                              value   => 'My Cookie',
                              expires => '+1h',
                           });

To retrieve the cookies from the client request:

 my $cookies = $OP->cookie->get_incoming;
Datasource

After defining your datasource in the OpenThought Config file, you can access it via the OpenPlugin datasource interface.

 $dbh = eval { $OP->datasource->connect( "datasource_name" ); };
 if ( $@ ) {
     $@->throw("Connection Error: $@\n");
 }

At this point, $dbh is a typical DBI database handle, and may be used as such:

 my $sth = $dbh->prepare("SELECT * from somewhere");
 $sth->execute;
 my $rows = $sth->fetchrow_hashref;

Now you have the first row of data from your database search stored in $row. See the DBI documentation for more information on interacting with databases and their results.

Exception

You can use this to throw an exception:

 $OP->exception->throw( "Some Error Message" );
Httpheader

You can send a typical text/html header like so:

 $OP->httpheader->send_outgoing();
Logging

To log a message to the current logging facility:

 $OP->log->warn("My log message");
Parameter

To retrieve a parameter sent from the browser to the client:

 $OP->param->get_incoming("parameter_name");
Session

Retrieve your session:

After properly calling OpenThought's deserialize function, your session_id is available as if it were a GET/POST parameter. You can retrieve it like so:

 $session_id = $OP->param->get_incoming( 'session_id' );

And then you can recreate your session:

 $session = $OP->session->fetch( $session_id );

You may make any modifications you wish, and once again save this session by saying:

 $session->{'key'} = "somevalue";
 $OP->session->save( $session );

You may want to perform the save *after* you've sent the headers and data back to the browser if you are able to. Saving the session isn't something that's usually necessary before the browser is given it's information. Choosing to save afterwards will get the data to the user faster.

BACKGROUND

We often find outselves waiting around for webpages to load. While a bit painful, many have gotten used to this. I haven't :-)

When going from one webpage to another, the amount of content that actually changes is often minimal. But if our browser has been told not to cache it -- which is often what happens with dynamic webpages -- we have to download again everything we've already downloaded on the previous page. As Jar Jar would say, "How wude!"

One of OpenThought's major features is that it allows you to set up an entire application using just one screen. After a given user loads that screen, they'll never have to load it again, for the life of that application. Content can be exchanged back and forth from the server and client, all without causing that page to refresh or reload. Upon receiving new content, OpenThought dynamically displays it within your browser.

And OpenThought offers a lot more than this. OpenThought can provide a full environment for your applications. It's not always practical to squeeze screens upon screens of data into one page. If you want to have several pages, thats fine. OpenThought provides you with an environment where session data is maintained for you, across any number of pages.

OpenThought gives you an easy way of tieing together multiple web pages, and allows you to build web applications which act like "real" applications. When an application is required, some people would decide to build it in Tk, Visual Basic, gtk, or any number of other systems which offer a visual interface. But then, your application is either not available on the web, or you have to create a seperate web interface to interact with the backend. And this seperate web interface no longer looks or acts like your application, it acts like an ordinary webpage, requiring several screens to get the same amount of content that your application offered in one.

By not ever needing to reload the page, OpenThought offers this application look and feel that's been missing from the web. This allows you to create just one interface, for both LAN and web use.

HOW IT WORKS

An OpenThought Application is made up of three parts. There is the OpenThought Engine, the OpenThought HTML Document, and the OpenThought Server Backend. You create the document and backend, the engine is provided for you. All three components are logically seperate, and never need to reside on the same computer.

You just installed the first part, the OpenThought Engine, when you installed this package. The OpenThought Engine is already created for you, the most you'll have to do with this is alter the config file.

The second part is the User Interface, known as the OpenThought HTML Document. This component is created by you, the application developer. The OpenThought HTML Document is made up the HTML screens that you provide for the user to interact with. This is where you begin to notice the differences with OpenThought applications. Any given screen interacts with the server in the background. These screens don't need to submit, don't need to go away, don't need to be refreshed. Whenever you want data to be displayed for the user, it can all be displayed dynamically.

The third part is the script on the server, known as the OpenThought Server Backend. This component is also developed by the application developer. This is the code sitting on the server that receives data sent to it by the user interface. Typically, this would be written in Perl. However, the means are available to write this in other languages. While no such plugins currently exist, eventually you will be capable of communicating with the OpenThought Engine via SOAP, Jabber, and other such things. Feel free to be adventurous and write the plugin yourself :-)

The secret to how OpenThought functions are hidden HTML frames and some funky JavaScript. These are used to submit data to the server, and the server's responses are also retrieved by these hidden frames. When data is received from the server, OpenThought's internal Javascript routines are used to properly handle it -- whether it needs to be displayed, whether it should be calling a JavaScript function, or any number of things.

Lets go step by step through a summary of what happens when using an OpenThought Application to dynamically display data.

1. The browser requests your OpenThought Application.
2. Your application gives the browser the HTML portion of your application.
3. The user can now interact with this document. As an example, lets say that he entered a person's last name, and clicked a submit button.
4. Instead of the OpenThought HTML Document being directly submitted as one would typically expect, the data it wants to send to the server is first converted to XML, and given to the hidden frame. The packet of data is then sent to the server via this hidden frame. The HTML the user already has in their browser is left completely in tact, and never reloaded or refreshed.
5. The OpenThought Server Backend (which you developed) now receives the data. It doesn't need to know how to parse this data though, and uses the OpenThought Engine to deserialize it.
6. The OpenThought Engine deserializes the packet, and returns it to the Application in the form of a hash (also sometimes called an associative array).
7. The Application is now free to do whatever it wants with this information. To continue our example, if we were sent a last name, perhaps we would do a search for this person's phone number in a database.
8. After we put together a dataset we would like to send back to the browser (a phone number in this case), we send it to the OpenThought Engine, in the form of a hash, to be serialized into a packet. For any data you wish to have displayed, the hash key that you use would correspond to a form element name in the OpenThought HTML Document.
9. The OpenThought Engine gives us back a packet, and we can now send this packet to the browser.
10. After receiving this packet, the browser immedietly handles the information for you -- and in this case, displays the phone number in the appropriatly named field. There is no programming on your part needed for this to work, OpenThought's JavaScript routines figure this out for you based on your hash keys as mentioned above.

This entire process actually runs extremely fast, and certainly competes with other types of applications. I've seen round trip times as low as 26 milliseconds, which certainly beats the pants off loading an entire webpage containing the same data :-)

GETTING STARTED

So far, this documentation has been all theory. But I know you're itching to create you own application, so we'll get into the nitty gritty now :-)

Genesis of an OpenThought Application

From here on out, we are assuming that you have successfully installed OpenThought, and that the demo program works correctly. If this is not the case, see the INSTALL file for instructions on getting OpenThought up and running.

Now, pick a name for your application, and let's get to work!

Location Location Location

The first thing you need to do is pick a location for your application. When OpenThought was installed on your system, an "OpenThoughtRoot" was chosen. OpenThoughtRoot is a directory under which all your OpenThought application executables (.pl files) need to be located. It is recommented that you keep your modules (.pm files) in a location not under your document root. On my RedHat system, I often use:

 # For .pl files
 /var/www/html/OpenThought/appname

 # For .pm files
 /var/www/site/App/Appname

So pick a spot for these files that works for you, and we're well on our way!

Create your User Interface

Next, create an HTML Document which will be your user interface. You can put this code anywhere you want, but I often use:

 # HTML Templates
 /var/www/site/templates/Appname

If you're into HTML, you'll like this part. If not.. well, it doesn't have to be too painful :-) You could even use applications like Macromedia's DreamWeaver and Fireworks to handle this visual design for you.

So, imagine all the things you would like to make available to your users, and start coding. You can use any number of form elements in your application. Also, you can use multiple div tags in conjunction with CSS visible / hide to give the look and feel of having tabs. With this, you can squeeze much more data into your application. However, implementing tabs does require a certain amount of HTML / CSS / Javascript knowledge.. if you aren't up on that, there's plenty of books, and even online tutorials on that subject. This is also where DreamWeaver and Fireworks can help you.

While developing your HTML code, you must give each of your form elements a unique name. For an example interface, feel free to use the demo-template.html included with OpenThought.

Now, you may start wondering, "How do I submit data from these forms to the server"? Data is sent after some sort of event is generated... such as clicking a button. When such an event occurs, you'll want to call one of the following Javascript functions:

 parent.CallUrl('application_url', form_element1, form_element2, ...)
   -- or --
 parent.FetchHtml('application_url', form_element1, form_element2, ...)

These functions get everything started.

See the section Sending Data to the Server for more information on the above two functions.

The parameter "application_url" is url of the application which will be receiving this data. An example of this is could be "http://www.foo.com/OpenThought/applications/demo.pl", or even just "/OpenThought/applications/demo.pl".

The parameters "form_element" allow you to send the contents of any number of form elements to the server. Simply list their names here. You defined their name when you used the <input name="foo"> attribute in your HTML. This tag's name is foo. At the time the event is generated, the current values of these elements will be taken, and it will all be sent to the server for you to process.

In addition to being able to send form elements to the server, you can also send an expression. To do so, in place of passing a fieldname as an argument, you can use 'foo=bar' as a parameter. Don't use spaces, and make sure you have it in quotes.

After that, your HTML is complete.

Create Your Server Backend

There are three phases of an OpenThought application, that your code needs to be able to handle. They can all be determined, simply by looking at what parameters your script was passed.

Phase 1. No Parameters

In the case that no parameters where sent to us, that means we're in the first phase. The browser is looking for us to give it the OpenThought base files, which can be done with:

 print $o->get_application_base();

This retrieves the base files from the OpenThought Engine, and sends them to the browser.

Phase 2. Parameter: OpenThought eq "1"

If you where sent the OpenThought parameter, and it's set to "1", the browser is looking for your OpenThought HTML Document. It's completely up to you on how to handle this, but I personally like to use OpenPlugin::Application's load_tmpl (which internally uses HTML::Template, and returns an HTML::Template object):

 if ( $OP->param->get_incoming( "OpenThought" ) eq "1" ) {
     my $template = $self->load_tmpl( 'templates/demo-template.html' );

     print $template->output;
 }
Phase 3. Parameter: OpenThought

If you are sent the OpenThought parameter, and it's contents are an XML packet, this means that the OpenThought application has finished loading, and that the user has sent us some data. All we need to do is process the data we were sent, and respond in an appropriate manner. For example:

 if(( $OP->param->get_incoming("OpenThought") &&
      $OP->param->get_incoming("OpenThought") ne "1" )) {

     $OP->param->set_incoming = $o->deserialize(
            $OP->param->get_incoming("OpenThought") );

     my $field_data;

     # If we were sent the parameter foo
     if ( $OP->param->get_incoming('fields')->{'foo'}) {
         $field_data->{comments} = "Thank you for sending foo!";
     }
     else {
         $field_data->{comments} = "Next time, send some foo!";
     }

     return $self->param('OpenThought')->serialize({ fields => $field_data });
 }

Phase 1 and 2 are often short, simple, and straightforward. The third phase is typically where most of your coding will be.

Now that you know all this, you can breathe a sigh of relief, because you won't have to deal with it much at all. With the addition of OpenPlugin::Application, and the usage of run modes, you won't typically need to perform checks to figure out which phase of OpenThought you are in. You'll simply know what to deliver to the browser when it asks to run a given run mode.

Now you can go ahead and write the code on the server end for your application.

It is highly recommended (but not necessary) that you use OpenPlugin::Application to build your application code. OpenPlugin::Application is strikingly similar to CGI::Application (okay, okay, so I just took CGI::Application, and changed every reference from CGI to OpenPlugin :-). It's very easy to understand, and the demo app that comes with OpenThought does use OpenPlugin::Application.

The following are some examples of things you may want to do in OpenThought.

This is an example of how you would send data to a select list in your user interface:

     $field_data->{'selectlist'} = [
                                     ['AIX'     ,'aix'    ],
                                     ['BeOS'    ,'beos'   ],
                                     ['Emacs'   ,'emacs'  ],
                                     ['HP_UX'   ,'hpux'   ],
                                     ['Linux'   ,'linux'  ],
                                     ['Netware' ,'netware'],
                                     ['OS/2'    ,'os2'    ],
                                     ['Plan 9'  ,'plan9'  ],
                                     ['Solaris' ,'solaris'],
                                     ['Windows' ,'windows'],
                                   ];

The hash key 'selectlist' is the name of the select element in the HTML document of the demo application, included with the OpenThought distribution. You then see two columns of data. The data on the left side is what is displayed in the select list, and the data on the right is the value assigned to that text. Said another way, the user with their web browser will see the data on the left. But when they click it, it's the data on the right which will be sent to your code on the webserver. For the technically inclined, the name of this data structure is a hash of arrays of arrays. For the not so technically inclined, just make sure you use the correct amount of brackets :-)

To send data to HTML elements other then select lists:

 $field_data->{'os'}          = "Linux";
 $field_data->{'creator'}     = "Linus Torvalds";
 $field_data->{'notes'}       = "World Domination 2001";
 $field_data->{'free'}        = "true";
 $field_data->{'cool'}        = "true";
 $field_data->{'goodlooking'} = "true";

This is just a simple hash reference. The key to the hash maps to a form field name in the HTML Document. Then whenever you send $field_data to the browser, the form field with the name 'os' will display the text 'Linux', the form field named 'creator' will display 'Linus Torvalds', etc.

You'll remember (from the demo app) that the last three elements we're dealing with here are each checkboxes. Checkboxes that are assigned the value "true" become checked when we send this data to the browser.

Now, to define data and have it sent to your browser, you could say something like:

 $field_data->{'os'}          = "Linux";
 $field_data->{'creator'}     = "Linus Torvalds";
 $field_data->{'notes'}       = "World Domination 2001";
 $field_data->{'free'}        = "true";
 $field_data->{'cool'}        = "true";
 $field_data->{'goodlooking'} = "true";

 return $self->param('OpenThought')->serialize({ fields => $field_data,
                                                 focus  => "selectlist" });

This will populate the fields in the HTML with the data you have in the $field_data hash reference, and will focus the element "selectlist".

The above code samples are all taken from the demo application which comes with OpenThought, named demo.pl. Feel free to take a look at it for a complete example of an OpenThought Application.

EXAMPLES

Continuing our use of OpenPlugin::Application, here are some examples of how you might build an OpenThought application. Some of these examples are borrowed from the demo application. Take a look at the demo app if you'd like more information.

The .pl File (mod_perl)

Creating your .pl file is simple. Just put it in your OpenThoughtRoot Directory. The file could contain something like:

 #!/usr/bin/perl -wT

 use strict;
 use lib "/path/to/pm/files";
 use Example();

 my $r = shift;

 my $example = Example->new( request => { apache => $r } );

 $example->run();

As for the "/path/to/pm/files" above, I often use /var/www/site for site specific pm files.

The .pm File (mod_perl)

The .pm files will contain the meat of your application. When using OpenPlugin::Application, there are several subroutines you'll need to have, which we'll go through. For more information, please see OpenPlugin::Application and the demo application.

 package Example;

 use strict;
 use base 'OpenPlugin::Application';
 use OpenThought();

 # Set up the run modes
 sub setup {
     my $self = shift;

     $self->run_modes(
            'mode1' => 'init_example',
            'mode2' => 'text_example',
            'mode3' => 'selectbox_example',
            'mode4' => 'radiobtn_example',
            'mode5' => 'html_example',
     );

     $self->start_mode('mode1');
     $self->mode_param('run_mode');
 }

 # Somewhat of a constructor -- called before setup()
 sub cgiapp_init {
    my $self = shift;
    my $OP   = $self->OP;

    $self->param( 'OpenThought' => OpenThought->new( $OP ));
    $OP->param->set_incoming(
                 $self->param('OpenThought')->deserialize(
                         $OP->param->get_incoming('OpenThought')));

    my $session = $OP->session->fetch(
                    $OP->param->get_incoming( 'session_id' ));

    $self->param( 'session' => $session );
 }

 # The default run mode
 sub init_example {
    my $self = shift;

    if( $OP->param->get_incoming('OpenThought')) {
        my $template = $self->load_tmpl('/path/to/demo-template.html');
        return $template->output;
    }

    else {
        return $self->param('OpenThought')->get_application_base();
    }
 }

OpenThought and CGI

Some users may find themselves needing to use CGI instead of mod_perl. This often will be because you don't have root on the machine, but I'm sure there are other reasons someone will be able to come up with :-)

Be forewarned that CGI does run slower then mod_perl. It might even be possible to install your own version of Apache / mod_perl in your user space on your ISP. If this still doesn't appeal to you though, the following will contain some examples on how to use OpenThought with CGI.

Using OpenThought under CGI very simple, you just have to be a bit more explicit about some things.

Assuming you currently have a .pl with the following line (taken from the above examples):

 my $example = Example->new( request => { apache => $r } );

You'll want to change it to read:

 my $example = Example->new( config => { src => "/path/to/OpenThought.conf" } );

There are two things that we just changed. We stopped passing $r to the Request plugin, and we put the path to the config file in there. The $r variable is only necessary when running under mod_perl. We definitely want to take that out when not using mod_perl. As for the config file path -- under mod_perl, we can state the path to the config file in the OpenThought-httpd.conf file. Under standard CGI, that won't work the way we want it to, so we need to explicitely pass the config file location in.

You also have the line in your .pm file:

 $self->param( 'OpenThought' => OpenThought->new( $OP ));

Change to:

 $self->param( 'OpenThought' => OpenThought->new( $OP,
                        { Prefix => '/path/to/prefix/dir' }));

This modification is again necessary under CGI. When using mod_perl, the Prefix is set in the OpenThought-httpd.conf file. Under CGI, you won't be able to use that file, so you'll need to exlicitely pass in the path when initiating your OpenThought object.

Text, Password, and Textarea HTML Elements

Client:

    <form name="myForm">

    <input type=text name=textbox_example>
    <input type=button name=search value='Click me!'
       onClick="parent.CallUrl( '/OpenThought/demo_app/text.pl',
                                'run_mode=mode2', 'textbox_example')">
    </form>

Server:

 sub text_example {
    my $self = shift;
    my $OP   = $self->OP;

    my $param = $OP->param->get_incoming('fields')->{'textbox_example'}";
    warn "We were sent $param";

    my $field_data;
    $field_data->{'textbox_example'} = "Blah blah blah";

    return $self->param('OpenThought')->serialize({ fields => $field_data });
 }

Selectbox HTML Elements

Client:

    <form name="myForm">

    <select name=selectbox_example>
        <option value="test">Test!
    </select>

    <input type=button name=search value='Click me!'
       onClick="parent.CallUrl( '/OpenThought/demo_app/selectbox.pl',
                                'selectbox_example')">
    </form>

Server:

 sub selectbox_example {
    my $self = shift;
    my $OP   = $self->OP;

    my $param = $OP->param->get_incoming('fields')->{'selectbox_example'}";
    warn "We were sent $param";

    my $field_data
    $field_data->{'selectbox_example'} = [
                                           [ "Example 1", "ex_one"   ],
                                           [ "Example 2", "ex_two"   ],
                                           [ "Example 3", "ex_three" ],
                                         ];

    return $self->param('OpenThought')->serialize({ fields => $field_data });
 }

Radio Button HTML Elements

Client:

    <form name="myForm">

    <input type=radio name=radio_example value="ex_one" checked><br>
    <input type=radio name=radio_example value="ex_two"><br>
    <input type=radio name=radio_example value="ex_three"><br>
    <input type=radio name=radio_example value="ex_four"><br>

    <input type=button name=search value='Click me!'
       onClick="parent.CallUrl( '/OpenThought/demo_app/radio.pl',
                                'radiobox_example')">
    </form>

Server:

 sub radiobtn_example {
    my $self = shift;
    my $OP   = $self->OP;

    my $param = $OP->param->get_incoming('fields')->{'radiobtn_example'}";
    warn "We were sent $param";

    my $field_data;
    $field_data->{'radio_example'} = "ex_one";

    return $self->param('OpenThought')->serialize({ fields => $field_data });
 }

Checkbox HTML Elements

Client:

    <form name="myForm">

    <input type=text name=checkbox_example>
    <input type=button name=search value='Click me!'
       onClick="parent.CallUrl( '/OpenThought/demo_app/checkbox.pl',
                                'checkbox_example')">
    </form>

Server:

 sub checkbox_example {
    my $self = shift;
    my $OP   = $self->OP;

    my $param = $OP->param->get_incoming('fields')->{'checkbox_example'}";
    warn "We were sent $param";

    my $field_data;
    $field_data->{'checkbox_example'} = "true";

    return $self->param('OpenThought')->serialize({ fields => $field_data });
}

HTML Example

Client:

    <h2>
      <div id="html_example">Old HTML</div>
    </h2>
     onClick="parent.CallUrl( '/OpenThought/demo_app/html.pl')">

Server:

 sub html_example {
    my $self = shift;

    my $html_data;
    $html_data->{'html_example'} = "New HTML";

    return $self->param('OpenThought')->serialize({ html => $html_data });
}

AUTHOR

Eric Andreychek (eric at openthought.net)

COPYRIGHT

The OpenThought Application Environment is Copyright (c) 2000-2002 by Eric Andreychek.

BUGS

Certain JavaScript functions don't seem to work properly under Internet Explorer 5.0. They work fine under 4.0, and fine under 5.5. I'm working to see if there is something that can be done about that.

Aside from that, bug hunting season has been good. All known bugs have been eradicated. If you happen to run across one, please let me know and I'd be more then happy to take care of it. But real hackers would send a patch ;-)

SEE ALSO

OpenPlugin

CGI::Application

Apache::Request

HTML::Template

OpenThought::XML2Hash

OPENTHOUGHT CONTRIBUTORS

* Greg Pomerantz (gmp216 at nyu.edu) - Put all sorts of time into that crazy bug which made Apache segfault on every request. It ended up being a problem with Apache and expat. Thanks to Greg for figuring that out!