Guppy Manual

Jørn Lind-Nielsen

DocBook conversion: Axel Guckelsberger

Version 6.0-1

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation, with no Invariant Sections, no Front-Cover Texts and no Back-Cover Texts. A copy of the license can be obtained from the Free Software foundation

Abstract

This manual documents how the web forms system Guppy can be configured and used in Pagesetter.


Table of Contents

1. Introduction
Introduction
2. Programming Model
3. Form Specification
4. Form Layout
Introduction
Reference
5. Event Handling
Event Handling
6. Plugins
Introduction
Plugin Structure
Pagesetter Methods
7. Internals

Chapter 1. Introduction

Table of Contents

Introduction

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:

  • Simpler code generation at the cost of design flexibility. There is although some overhead when creating a new form. It does require the programmer to create three new files for each form.

  • Automatic input validation.

  • Automatic handling of multi-record entries with insertion and deletion af new rows.

  • Automatic handling of multi-record nested entries in a tree structure.

  • Wysiwyg HTML input using HtmlArea.

  • Date selection using a nice little JavaScript date picker.

  • Separation of form specification and layout enabling "untrusted" users to change the layout without being able to influence the actual functionality.

  • Consistent look and feel in all web forms.

Chapter 2. 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.

Chapter 3. Form Specification

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

Chapter 4. Form Layout

Table of Contents

Introduction
Reference

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).

You must specifier either "text" or "html" explicitely in the layout for fields of those types.

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.

Chapter 5. Event Handling

Table of Contents

Event Handling

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.

Chapter 6. Plugins

Introduction

The Guppy system allows anybody with some PHP and OO programming experience to add extra input types in addition to those that already exists. The system is based on ideas from Smarty and ASP.NET, but not yet complete in anyway. It has already been used to implement the "datetime", "e-mail", and "url" inputs available in Pagesetter.

The Guppy/Pagesetter combination has not been separated completely, so the plugins support both callback methods for Guppy and Pagesetter. Guppy in itself does not restrict the plugins much in terms of what they can do, but Pagesetter imposes some restrictions.

The idea is to instantiate a class, named like the plugin name, for each of the inputs that are plugin based, and then call various methods on these objects for different puposes. This can be rendering, decoding, and validation.

Plugin Structure

The structure of a plugin is as follows, where all variables and methods are inherited from GuppyInput:

class GuppyInput_MyPlugin extends GuppyInput
{
  var $ID;          // HTML document ID
  var $name;        // Base name for form elements
  var $title;       // Title (label) as specified in XML files
  var $value;       // Free place to store result of decoding
  var $mandatory;   // Mandatory setting as specified in XML files
  var $readonly;    // Readonly setting as specified in XML files
  var $hint;        // Hint setting as specified in XML files
  var $width;       // Width setting as specified in XML files
  var $height;      // Height setting as specified in XML files
  var $error;       // Free place to store error message

  function render($guppy)
  {
    // Use echo, print, and similar to output whatever 
    // HTML is needed for your plugin.
  }

  function decode()
  {
    // Read POST variables and return a single value representing
    // what the user has entered.
  }

  function validate()
  {
    // Return true or false depending on whether or not
    // the input was valid
  }

  function getErrorMessage()
  {
    // Return error message in case validate() returns false
  }
}
    

What happens is as follows:

  1. Guppy renders tables and other internal stuff. Every time it needs to render a plugin, it calls $plugin->render() for the selected plugin. Only one instance of the plugin class is created for each input during the rendering phase.
  2. The inputs something and submit the forms. Guppy decodes all the inputs and calls $plugin->decode() to get the values of from the plugins. Only one instance of the plugin class is created for each input during the decode phase. The decode() function must return the decoded value.
  3. Right after the decoding, Guppy calls $plugin->validate() on the same plugin instance. If it returns true then everything is fine. If it returns false then $plugin->getErrorMessage() is called to retrieve the error.

Check the guppy/plugins directory for examples.

Pagesetter Methods

Pagesetter depends the following methods of the plugins:

class GuppyInput_MyPlugin extends GuppyInput
{
  // ... Guppy stuff before this ...


  function active()
  {
    // Return true or false indicating whether or not this plugin
    // should be used in Pagesetter.
  }

  function getTitle()
  {
    // Return title for this plugin
  }

  function getSqlType()
  {
    // Return MySQL type definition for a database column where
    // the data from this plugin can be stored
  }

  function getSqlFormat()
  {
    // Return SQL expression for selection of data for this plugin.
    // Use "$columnName" as placeholde for the columnName
    // Return NULL if no special attention is needed.
  }
}
    

Please note that Pagesetter can only use *one* database column for the data of your plugin.

Chapter 7. 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.