Tutorial - Creating a Web Application in Oak

Note

I don't have enough time to maintain this tutorial... so it is incomplete, but feel free to ask for help in my email: daniel@ruoso.com

Index

  1. Creating the executable file
  2. Creating the XML for the web pages
    1. Increasing Reusability: Creating your own components
  3. Creating the modules to catch the events
  4. Creating control modules
    1. Using autentication
    2. Using transactions
  5. Creating entity classes
    1. Creating a Datamodule with the DB Connection
    2. Using a config file
    3. Using syslog
    4. Creating the entity class itself

Creating the executable file

In this tutorial, we are going to create a small application that manages a clients, allowing listing, editing, creating and deleting. In this first step, we will add just the first page of the application.

FILE: clientmanager.cgi

	#!/usr/bin/perl
	
	use strict;
	use Oak::Web::Application;

	my $app = new Oak::Web::Application
          (
	   startPage => ["startPage","startPage.xml"],
           default => "startPage"
          );

        $app->run("CGI");
      

I will not explain simple perl declarations in this tutorial, so I will start at the constructor of the Oak::Web::Application object.

	my $app = new Oak::Web::Application
          (
	   startPage => ["startPage","startPage.xml"],
           default => "startPage"
          );
      

This constructor receives a hash with the top-level components of the application, in our case, just the startPage.

	   startPage => ["startPage","startPage.xml"],
      

The key of the hash MUST be the component name, the component will be acessible in the $::TL::startPage for future use. The value is a reference to an array containing the class name of this page (used to process the events) and the filename of the xml file.

           default => "startPage"
      

The special key "default" is used to describe which of the components have to be shown when a request with no params is passed to the cgi (the first access to the application)

        $app->run("CGI");
      

This command initiates the application. If you want to understand the details, see the Oak Reference Guide for the Oak::Web::Application class.

Creating the XML for the web pages

In the first step we created the cgi file, but it does nothing alone, it needs the XML of the components to ready to do something. We will create the startPage.xml file, which describes a dummy entrance page (we will improve it later to implement authentication).

In this step you can use the Forest Web Application Builder to create your XML files, As it has a preview of the XML, it can help to create a preetier page. Tip: Ask the designer to create the layout of your application before you start your XML files.

FILE: startPage.xml


<main>
    <prop name="linkrel" value="link rel='stylesheet' type='text/css' href='/style.css'" />
    <prop name="name" value="startPage" />
    <owned name="body">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Body" />
        <prop name="parent" value="startPage" />
    </owned>
    <owned name="br1">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Br" />
        <prop name="left" value="2" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="0" />
    </owned>
    <owned name="br2">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Br" />
        <prop name="left" value="2" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="1" />
    </owned>
    <owned name="content">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Div" />
        <prop name="class" value="content" />
        <prop name="parent" value="body" />
        <prop name="top" value="1" />
    </owned>
    <owned name="footer">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Div" />
        <prop name="class" value="footer" />
        <prop name="parent" value="body" />
        <prop name="top" value="2" />
    </owned>
    <owned name="formLogin">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Form" />
        <prop name="parent" value="content" />
    </owned>
    <owned name="h1">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::H1" />
        <prop name="caption" value="Login Form" />
        <prop name="level" value="1" />
        <prop name="parent" value="header" />
    </owned>
    <owned name="header">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Div" />
        <prop name="class" value="header" />
        <prop name="parent" value="body" />
    </owned>
    <owned name="labelPassword">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Label" />
        <prop name="caption" value="Password:" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="1" />
        <prop name="type" value="password" />
    </owned>
    <owned name="labelUserName">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Label" />
        <prop name="caption" value="User Name:" />
        <prop name="parent" value="formLogin" />
    </owned>
    <owned name="password">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Input" />
        <prop name="left" value="1" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="1" />
    </owned>
    <owned name="submit">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Input" />
        <prop name="ev_onClick" value="$::TL::startPage-&gt;on_submit_click()" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="2" />
        <prop name="type" value="submit" />
        <prop name="value" value="Log In" />
    </owned>
    <owned name="userName">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Input" />
        <prop name="left" value="1" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="0" />
    </owned>
</main>

      

The XML file has the main tag, which defines the begin and end of the file.

<main>
...
</main>
      

Inside the main tag, there are the top-level component's properties and the owned components. The properties are defined like this:

    <prop name="linkrel" value="link rel='stylesheet' type='text/css' href='/style.css'" />
    <prop name="name" value="startPage" />
      

The owned components are defined with the owned tag:

    <owned name="userName">
        <prop name="__CLASSNAME__" value="Oak::Web::HTML::Input" />
        <prop name="left" value="1" />
        <prop name="parent" value="formLogin" />
        <prop name="top" value="0" />
    </owned>
      

When desingning a XML, you must understand what are the "parent", "top" and "left" properties. The "parent" property defines in which component this component is inside. The "top" and "left" properties are used to order the components wich have the same parent.

The Oak::Web::Page class will not handle BODY or FRAMESET tags, you MUST create a Oak::Web::HTML::Body or a Oak::Web::HTML::Frameset inside the page, as you can see in this XML.

If you can't wait until the next step, you can run your application, but to do this you will need to change the "startPage" to "Oak::Web::Page" (Yes, startPage will be a descendant of Oak::Web::Page). The result will be something like (html version):

Content-type: text/html

&<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE></TITLE>
<link rel='stylesheet' type='text/css' href='/style.css'>
</HEAD>
<BODY>
<DIV class="header">
<H1>Login Form</H1>
 </DIV>
  <DIV class="content">
<FORM name="formLogin">
<INPUT TYPE="HIDDEN" NAME="__owa_origin__" VALUE="startPage">
<LABEL>
User Name:</LABEL>  <INPUT name="userName">
  <BR>  <LABEL>
Password:</LABEL>  <INPUT name="password">
  <BR>  <INPUT type="submit" name="submit" value="Log In">
     </FORM>
 </DIV>
  <DIV class="footer">
</DIV>
 </BODY> </HTML>
      

I will not describe the creation of each page of this sample application. The files are available into the tests/Tutorial directory inside the Oak module into the CVS (maybe I will distribute it as a package).

Increasing Reusability: Creating your own components

When using Oak, you will see that some elements can be designed as components, and this is, actually the better way to increase reusability and simplify your code. In the clientList page, for example, you have the list with the clients. This list is a table where each <TR> element represents one client. And each line has a link that opens the information about that client. Which is the component way of doing this table? Simple. You have two choices, one is to create a single component that shows the table with all its lines, and this component will have a property named "clients" that holds an array or a hash with the data that will be shown. The other way is to create a component that lists just one client and a component that lists many "clientLines". The decision of when to use one single component or when to split this component in subcomponents is yours. According to the complexity and probability of reuse of that subcomponent.

In this tutorial, I will choose to create the component clientLine and the component clientTable, because this way I can show the flexibility of the architecture.

The clientLine component

The first thing to do when creating a component is to design how the component look like, in this case:

<TR><TD><A HREF="(go to the editClient page">Name of the client</A></TD><TD>EMAIL</TD><TD>PHONE</TD></TR>
      

Then you have to decide which components you are going to use inside your component. In this case we will use just a Oak::Web::Additional::ActionLink and a Oak::Web::Additional::Template. The reason I don't use a component for the TR, the TDs and the other texts is simply because it doesn't need to be. Remember, using a subcomponent implies using more memory, more cpu cycles, and if you can do it well using plain html, it will be better.

So, now we need to decide the properties and events of this components. In this sample component we will use class (used for css), clientId, clientName, clientPhone, clientEmail and the event ev_onClick. We will use plain html in this component, except for the name of the client, that will be a Template inside a ActionLink.

A component usually is a descendant of Oak::Web::Visual or Oak::Web::Container, a container is a component that has other components inside it, but he doesn't own these components. It looks for the components in his owner that have this container as parent.

A visual component is a simple component. That shows data and receives data, it can own other components but whoever use this component doesn't have to know about that. Both the components we are going to create here are Oak::Web::Visual descendants, because they will own other components, but this is transparent.

A Visual component has two important methods, the show() and the receive_cgi($cgi). The show method is called when its time to print, the component must print itself to the selected filehandle (just call print $string). The receive_cgi method is called when the owner of this component (or the owner of the owner ...) receives a message(POST => $cgi) from the Application object. This method must use the CGI object it receives to change its properties to represent the information the user has filled in.

So, there it goes the clientLine component:

FILE: clientLine.pm

package clientLine;

use strict;
use base qw(Oak::Web::Visual);

sub show {
	my $self = shift;
	$self->SUPER::show;
	$self->_create_action_link;
	print "<TR";
	print " CLASS=\"".$self->get("class")."\"" if $self->get('class');
	print "><TD>";
	$self->get_child($self->get('name')."___link___")->show;
	print "</TD>";
	print "<TD>".$self->get("clientPhone")."</TD>";
	print "<TD>".$self->get("clientEmail")."</TD></TR>";
}

sub _create_action_link {
	my $self = shift;
	my $origin = $self->get('origin') || $self->{__owner__}->get("name");
	return if exists $self->{__owned__}{$self->get("name")."___link___"};
	new Oak::Web::Additional::ActionLink
	  (
	   OWNER => $self,
	   RESTORE =>
	   {
	    ev_onClick => $self->get("ev_onClick"),
	    name => $self->get("name")."___link___",
	    origin => $origin
	   }
	  );
	new Oak::Web::Additional::Template
	  (
	   OWNER => $self,
	   RESTORE =>
	   {
	    name => $self->get("name")."___text___",
	    type => "string",
	    source => $self->get("clientName"),
	    parent => $self->get("name")."___link___"
	   }
	  );
}

sub receive_cgi {
	my $self = shift;
	my $cgi = shift;
	$self->_create_action_link;
	my $alink = $self->get_child($self->get("name")."___link___");
	$alink->receive_cgi($cgi);
	if ($alink->{__events__}{ev_onClick}) {
		$self->{__events__}{ev_onClick} = 1;
	}
}

1;
      

The clientTable component

The clientForm component

There is another component in this application. The clientForm, that shows the information of the client, this form is used in the "editClient" page and in the "addClient" page.

Creating the modules to catch the events

Creating control modules

Using autentication

Using transactions

Creating entity classes

Creating a Datamodule with the DB Connection

Using a config file

Using syslog

Creating the entity class itself