Guppy Development

(C) Jørn Lind-Nielsen 2004

Contents

Introduction

The Guppy framework is a web forms handling system that adds a few extra features to the standard HTML forms handling. It allows the programmer to specify a web form using a few XML files—two for each form (plus an additional PHP handler file). The use of a relatively simple XML specification removes the burden of creating a lot of complex HTML and JavaScript from the programmer at the cost of design flexibility.

With Guppy one can design different components: cards that show a single record, tables that show multiple records, and trees that shows multiple nested records. Each kind of component may contain one or more fields of different kinds. Fields can currently only be input fields with support for booleans (checkboxes), integers, reals, dates, times, strings, html, and selectors. Most of the input fields can be marked as mandatory which forces the user to enter something in the field.

The benefits of Guppy are:

Programming Model

The Guppy system is event based. The programmer supplies a form specification XML file, a layout XML file, and a handler PHP object. The framework then renders the HTML and based on the user input it calls various event methods in the PHP handler.

The event handler object must inherit from the GuppyDecodeHandler class. It's event handling methods are passed an event object describing the current event. The inheritance from GuppyDecodeHandler ensures that $this has a reference to a Guppy command handler (the "commander") which must handle all output from any event handler.

Form Specification

To be written ... this will descript how a Guppy form is specified (without layout information).

Form Layout

Introduction

This section deals with the format of the layout definition file. The layout XML is a recursive structure that allows you to place your components in a grid structure (a table) much like done in a normal HTML table. A layout begins and ends with the tag "layout":

<?xml version="1.0"?>

<layout>
  ... layout ...
</layout>

The layout tag begins a grid (in the same way as a table tag does it in HTML) so the content must define the rows of this grid. This is done with the row tag:

<?xml version="1.0"?>

<layout>
  <row>
    ... row layout ...
  </row>
</layout>

Now we are ready to put some content in the rows. The most import tag is the component tag which specifies where a component should be placed. The component tag must specify which component it refers to. In the examples here we use the Pagesetter "edit publication" form which defines only one component named "pubedit".

If we drop the initial xml tag we now get:

<layout>
  <row>
    <component name="pubedit">
      .. component layout ...
    </component>
  </row>
</layout>

Inside the component tags we can sprinkle out field tags where ever we want to place one of the input fields available for the component. But before we do that we must first setup a layout structure for the component. Again we use the layout tag which places the layout in grid mode, so we must also add rows to place our fields in:

<layout>
  <row>
    <component name="pubedit">
      <layout>
        <row><field name="title"/></row>
        <row><field name="text"/></row>
      </layout>
    </component>
  </row>
</layout>

As you can see you must also supply a field name that refers to one of the fields specified for that component. You need not consider what kind of field it is since Guppy already nows that and will create what ever is needed. Almost. You do need to specify how you want a text field to display. It can be displayed as a one line text input field, a textarea tag, or as an HTML editor using HTMLArea. Use the view attribute on the field tag for this. In our example we want the "text" field to be shown as an HTML editor. Let us resize the input field a bit at the same time by setting a height attribute:

<layout>
  <row>
    <component name="pubedit">
      <layout>
        <row><field name="title"/></row>
        <row><field name="text" view="html" height="300"/></row>
      </layout>
    </component>
  </row>
</layout>

It further more seems sensible to require that the title must be entered. So we mark the "title" field as mandatory, and set the width to 400 pixels at the same time:

<layout>
  <row>
    <component name="pubedit">
      <layout>
        <row><field name="title" mandatory="true" width="400"/></row>
        <row><field name="text" view="html" height="300"/></row>
      </layout>
    </component>
  </row>
</layout>

At last we also want to be able to interact with out form, so we must add some buttons. In this example we add the three core buttons that Pagesetter supplies—the preview, Update, and Cancel buttons.

<layout>
  <row>
    <component name="pubedit">
      <layout>
        <row><field name="title" mandatory="true" width="400"/></row>
        <row><field name="text" view="html" height="300"/></row>
      </layout>
      <buttonsBottom>
        <button name="corePreview"/>
        <button name="coreUpdate"/>
        <button name="coreCancel"/>
      </buttonsBottom>
    </component>
  </row>
</layout>
Beware that this is the only way to position the specific Pagesetter buttons. You must not move them to any other place in the structure. This is to allow Pagesetter to insert dynamic workflow actions (for which the location is hardcoded).

Reference

button

The button tag places a single button in the layout. You must at least specifiy the name of the button using the name attribute.

Attributes
name Name of the referenced button.
title Title for this button. If no title is supplied then the one defined in the specification is used.
hint A hint describing what the button is for. If no hint is supplied then the one defined in the specification is used. The hint is used as a "title" attribute in the generated HTML.

buttonsBottom

The buttonsBottom tag defines a singe-row container for buttons. It must be placed inside a component tag. Buttons placed inside the buttonsBottom tag will be placed below the associated component pane.

buttonsTop

The buttonsTop tag defines a singe-row container for buttons. It must be placed inside a component tag. Buttons placed inside the buttonsTop tag will be placed above the associated component pane.

field

The field tag places a single input field in the layout.

Attributes
kind The kind of field. Defaults to "input" but can also be "action" to reference an action.
name Name of the referenced field.
title Title for this input field. This will be used as a label placed in a separate title tag immediately before the input field. If no title is supplied then the one defined in the specification is used.
width The width of the field in pixels.
height The height of the field in pixels.
colspan Column span as for a HTML td tag.
view Defines how the field should be displayed. Can be "string" (default), "text", or "html". The result is either a simple one line input (string), a textarea (text), or an HTML editor (html).
hint A hint describing what the input field is for. The exact rendering of the hint is not yet defined.
mandatory Forces the user to enter something in the field. Must be set to "true" or "yes".
readonly Inhibits the user from entering something in the field. Must be set to "true" or "yes".

island

The island element is a grid container that adds a visual box around it's contained elements.

Attributes
title Title for this button. If no title is supplied then the one defined in the specification is used.
colspan Column span as for a HTML td tag.

layout

The layout element defines a grid container in which rows must be placed.

row

The row element defines a single row in a grid and must contain cell elements like the component tag or a nested layout.

title

The title element adds a label for some input field. To do so it must refer to an input element by name. The title of that element is then placed where the title element is. All input fields automatically expands to a title element followed by an input element unless otherwise specified.

Attributes
title Title for this button. If no title is supplied then the one defined in the specification is used.
colspan Column span as for a HTML td tag.

Event Handling

All usage of Guppy starts with a call to guppy_decode to see if the script has been initiated from Guppy or from else-where. If the script is not started as a result of a Guppy event then guppy_decode will return the boolean value true. If it is a result of a Guppy event then it will return an associative array with the following elements.

action
This is an array that describes the current event. See below for sub-properties.
data
This is an array identical to the data array passed to guppy_open, but including whatever changes the user may have done.
extra
This is an array with the values passed in the "extra" property of the array passed to guppy_open. This part of the data can be used freely to store any extra data needed to handle the form.

The action object contains the following properties:

kind
A string that describes the event kind. This list is rather long and can be found in the next section. Most of the time the action kind is identical to the name of the method that handles the event.
component
Name of the component from which the action was initiated, e.g., the component in which a button is located.
rowIndex
Row index for an action that influences a table or tree component.
button
Name of activated button.
buttonKind
Kind of activated button.
action
Name of activated action.
clickHeader
menuAction
treeXXX
... lots of different actions.

Internals

Parsing of the XML files for forms specification and layout is handled in guppy_parser.php. The code here creates a large associative array that describes the form. The data is stored in a PHP session variable. New attributes for the XML structure must be parsed here.

Rendering of the HTML and handling of user input is done in guppy.php. New attributes returned from the XML parser must be handled here.

99% of the stuff that is PostNuke related is implemented in guppy_postnuke.php in the hope that this makes it possible to reuse Guppy in another framework.