5. Building Gizmos

As described in Section 1, the Gizmo class is the parent class for most of the "information objects" used in the Metadot Portal Server, in particular those objects which can be used to create content on "Category" pages. All Gizmos store their data in the instance table.

The GizmoBuilder class, which subclasses Gizmo , provides a set of interfaces and methods to facilitate Gizmo construction. In particular, the GizmoBuilder class will provide a default create/modify interface for the gizmo, so that new gizmos can be created with very little new code.

All new Gizmos should subclass GizmoBuilder .

All gizmos (that is, subclasses of GizmoBuilder) must be placed in the
<metadot>/metadot/Gizmo subdirectory;
this allows them to be automatically recognized by the system. All `visible' gizmos in the Gizmo subdirectory will be listed in the "Add New.." pull-down menu that is displayed when site editing is enabled. There are two ways to indicate which gizmos are visible and should be listed in the Add menu. See Section 5.4 for more information. (See the Users' and Administrator's guides for more information about the existing gizmos).

This section describes how to build new gizmos by subclassing GizmoBuilder. In addition, it is of course possible to subclass existing gizmos.

5.1 The instance table

All gizmos "encapsulate" a record in the instance table. All but a set of reserved fields may be used to store data for a gizmo. The following fields in the instance table are reserved:

+-----------------+--------------
| Field           | Type         
+-----------------+--------------
| UID             | int(11)      
| IID             | int(11)      
| ParentIID       | int(11)      
| IsA             | varchar(20)

In addition to its required fields, the instance table has a number of free fields of different data types which may be used to store gizmo data. The free fields are the following. The first group of fields below, such as "name", "description", etc. should be used in a semantically consistent manner across gizmos; this allows default display methods to work consistently. See existing gizmos for more information. (However, these fields are not required to be used consistently; only the fields listed above are reserved). The second group of fields below are "extras" which may be used for any purpose.

----------------+--------------
Field           | Type         
----------------+--------------
Name            | varchar(80) 
Description     | text         
Cool            | varchar(5)   
URL             | varchar(222) 
Keywords        | varchar(255) 
showfrom        | datetime     
file1           | varchar(255) 
longdescription | text         
 
t1              | text         
t2              | text         
t3              | text         
t4              | text         
t5              | text         
c1              | varchar(111) 
c2              | varchar(111) 
c3              | varchar(111) 
c4              | varchar(111) 
c5              | varchar(111) 
d1              | date         
d2              | date         
d3              | date         
d4              | date         
d5              | date         
i1              | int(11)      
i2              | int(11)      
i3              | int(11)      
i4              | int(11)      
i5              | int(11)      
t6              | text         
t7              | text         
t8              | text         
t9              | text         
t10             | text         

Of course, it is always possible to create auxiliary tables for use with a new gizmo in addition to using the free instance fields.

5.2 The UploadsManager Utility

Older versions of Metadot allowed only one file "attachment" (or associated file) per gizmo. The location of this file was stored in the file1 field of the instance record for the object.

In Metadot 5.0 and higher, file attachments are now managed by the UploadsManager class. The UploadsManager handles both access-controlled and `public' files. Access-controlled uploads are put in a non-web-server-accessible directory, and accessed (downloaded) via a cgi query, which checks that the user has permission to view the download. Public files are put in a web-server-accessible directory. There can now be multiple associated files, of either type, per gizmo. The file information is now stored in the uploads database table. All new gizmos should use the UploadsManager interface for any associated files, as described in the example below.

5.3 The GizmoBuilder class

The GizmoBuilder class subclasses the Gizmo class. It provides an easy way to create Gizmos.

GizmoBuilder provides a set of methods to

The developer then just needs to:

Using GizmoBuilder , a new gizmo can be created in a few steps, listed below. There are also some optional steps that can be taken to modify Gizmos if developers wish to change the default settings provided to them by GizmoBuilder .

5.3.1 Write a constructor and specify field mappings

Gizmos can use all the fields of the instance database table. So, in the constructor, specify which of those fields you will be using in your gizmo. At the same time, specify labels for those fields, and specify which fields are required. By doing so, you allow the Metadot framework to automatically generate a form for your gizmo that asks people to fill in the appropriate fields, and ensures that required fields are actually filled in.

5.3.1.1 Mapping instance fields to form fields

The following code from GizmoBuilder shows the default form type mapping used by GizmoBuilder , which associates fields in the instance table with a " formtype ". This formtype is translated, by the GizmoBuilder when constructing a gizmo create or modify form, into a form input element with specific characteristics. For example, a formtype of "smalltext" corresponds to an input element of type text and maxlength 10, and a formtype of "longtext" corresponds to an input element of type textarea . See the GizmoBuilder code for translations of all the formtypes.

sub _initialize {
    my $self = shift;
    $self->{_intro_text_string} = "";
 
    ## set the form type for each field
    $self->{fields}->{name}->{formtype}             = "text";
    $self->{fields}->{description}->{formtype}      = "mediumtext";
    $self->{fields}->{long_description}->{formtype} = "longtext";
    $self->{fields}->{cool}->{formtype}             = "radio";
    $self->{fields}->{url}->{formtype}              = "text";
    $self->{fields}->{keywords}->{formtype}         = "mediumtext";
    $self->{fields}->{show_from}->{formtype}        = "smalltext";
    $self->{fields}->{t1}->{formtype}               = "longtext";
    $self->{fields}->{t2}->{formtype}               = "longtext";
etc...

When building a new gizmo, if the defined form types are sufficient, then it is only necessary to define the set of instance record fields that will be used for the gizmo (in addition to the required fields), and to associate a form label with each of them. This is accomplished by the set_field_info method. It takes arguments a set of triplets, where the first element in each triplet is the name of the field (e.g. `name' or `description'), the second element in each triplet is the label to be used in the gizmo's create and modify forms for that field, and the third is the number 0 or 1. A `1' means that the form field is to be treated as `required', meaning that if the user must fill it in.

    $self->set_field_info (
        "name",        "<b>Calendar Name</b>",              1,
        "description", "<b>Description</b>",                0,
        "keywords",    "<b>Keywords and Synonyms</b>",      0,
        "c3",          "<b>Show Event Quick List</b>",      0,
        "c1",          "<b>Title row background color</b>", 0,
        "c2",          "<b>Footer background color</b>",    0,
        );

The gizmo developer may also want to redefine some of the preset form types for the gizmo fields. This can be done via the set_field_type method. For example, the code below redefines the `c3' field to be of type `checkbox' instead of the default `smalltext'.

    $self->set_field_type('c3','checkbox');

The allowable formtypes are the following:

  • · smalltext, hidden, text, mediumtext, longtext, radio, checkbox, select, file , and other .

The `other' formtype is a special case. No defaults are used; the form element HTML must be explicitly specified. `Other' form fields are set using the set_field_form_text method. It takes two arguments: the field name, and the form text to use. The method sets formtype to `other'. For example, the following two lines set the `c1' and `c2' fields to HTML generated by a special-purpose instance method for the example gizmo.

    $self->set_field_form_text('c1', $self->_get_form_text('c1'));
    $self->set_field_form_text('c2', $self->_get_form_text('c2'));
5.3.1.2 Example of GizmoBuilder subclass constructor

The constructor for a GizmoBuilder subclass should define the form field info and do some other bookkeeping. Here is an example constructor (the line numbers, of course, would not be used in actual code). This is in fact the Item constructor. The Item gizmo is a simple gizmo which allows the user to specify a name, abstract (short description), longer description, and and optional downloadable file attachment.

1.  sub new {
2.      my $proto = shift;
3.      my $id = shift;
4.
5.      my $class = ref($proto) || $proto;
6.      my $self;
7.
8.      if (defined ($id)){
9.       $self  = $class->SUPER::new($id);
10.     } else {
11.       $self  = $class->SUPER::new();
13.       $self->{cool}='No';
14.     }
15.
16.     $self->{is_a} = __PACKAGE__;
 
17.     $self->set_field_info (
18.         "name",        "<b>Item Name</b>", 1,
19.         "url",         "<b>URL</b> <i>e.g. http://www.yourcompany.com</i>", 0,
20.         "description", "<b>Description</b>",   0,
21.        "item_attachment_file",   "<b>Attachment file</b>",  0,
22.         "keywords",    "<b>Keywords and Synonyms</b>", 0,
23.         "cool",        "<b>Star this Item</b>",  0,
24.         );
 
25.     $self->set_field_type( 'item_attachment_file', 'user_selected_upload' );
 
26.     bless ($self, $class);        # reconsecrate
27.     return $self;
28. }

Here is a line-by -line explanation of the code above.

1. This line names the subroutine. The new subroutine will be called by the metadot framework when it instantiates your gizmo

2. This line assigns the name of the class to the $proto variable

3. This line assigns the id (an optional parameter) to the $id variable

5. This line ensures that if the class name passed in was actually a reference to a class, we use the actual class name.

8-14. These lines are important: if an id is passed in, that means we are instantiating a gizmo which has previously been created and then stored into the database. If an id is not passed in, we are instead instantiating a new gizmo.

13. This line means that gizmos are not starred by default

16. This line takes the package that the gizmo is in (such as Item, or Table), and assigns it to the is_a instance variable

17-24. These lines specify which fields of the instance table are going to be populated for this particular gizmo class, and the form field labels to use with them. The first parameter is the name of the instance table field. The second parameter is the label, used when displaying forms when the item is created or edited. The third parameter is set to 1 if it is a required field. Otherwise, it is set to zero. (If a field is required, then the form submission will generate an error if it is left blank). The exception in this list is the "item_attachment_file" field. It does not map to an actual instance table field, but instead will be mapped to an entry in the uploads table. This is set via the code in line 25 (see below).

25. This line indicates that the `item_attachment_file' field is of type `user_selected_upload', which means that it will be handled by the UploadsManager . This example just shows one field of type `user_selected_upload', but potentially a gizmo may support multiple uploads fields.

26. This line makes $self into a perl object.

5.3.2 Define display methods for the gizmo

In addition to the constructor, a subclass of GizmoBuilder should define the following methods:

    show_summary
    show

These are the canonical display methods for a new Gizmo. If no operation on a gizmo is specified, `show' is the default.

The simple example below shows `hard-coded' generation of HTML. It is, of course, possible to use other approaches to generate the object's "display" HTML instead. For example, some Metadot gizmos use a Template-Toolkit template to build their HTML.

 

show_summary is used to render a `list view' of the gizmo in its parent page, as in the screen shot below. Often, the `summary view' for a gizmo will include a link that when clicked on invokes the gizmo's show method. See the show_summary method in <metadot>/Gizmo/Discussion.pm for a relatively complex example.

 

 

The show method is used to display the `full' view for a Gizmo, as in the screen shot below. For example, for a Discussion gizmo, the `show' method is called when a user clicks on one of the discussion links on a category page.

In writing the display methods, you can use getter methods to retrieve the data for the gizmo, where the data is persisted as a field in the instance table, and is instantiated by the GizmoBuilder class. A list of all getter and setter methods is given in the SYNOPSIS of the POD documentation for GizmoBuilder.

Below is an example of a show method, again for the Item gizmo. This method displays the Item's name, and (if set), its abstract, description, and a download link to the gizmo's file attachment.

1.    sub show {
2.    my $self = shift;
3.
4.    my $name            = $self->get_name();
5.    my $description     = $self->get_description();
6.    my $url             = $self->get_url();
7.    my $keywords        = $self->get_keywords();
8.    my $file            = $self->get_file_1();
9.    my ( $file, $file_icon ) = $self->get_upload_filename_icon_and_url( {
									 id => $self->id(), 
									 isa => $self->is_a(),
									 field_name => 'item_attachment_file',
									} );
 
10.    my $cool_icon       = $self->get_cool_icon();
11.    my $longdescription = $self->get_long_description();
12.    my $html            = '';
13.
14.    $html .= ($url) ? 
    "<a href='$url'>$name</a>\n" : "<b>$name</b>\n";
15.    $html .= $cool_icon;
16.    $html .= "<BR>$description\n" if ($description);
17.    $html .= "<P>$longdescription\n" if ($longdescription);
18.    $html .= "<BR><BR>File attached: $file $file_icon\n" if ($file);
19.    $html .= "<BR><BR>Synonyms: $keywords\n" if ($keywords);
20.
21.    return $html;
22. }
 

Line-by-line explanation:

1. This line names the subroutine

2. This line assigns the object to the $self variable. $self is analogous to the 'this' object in Java.

4-11. These lines retrieve values from the object into variables. Note line 9. in particular. This line handles the special case of a file attachment associated with the object via its "item_attachment_file" field. Retrieval of the file attachment information is handled ultimately via the UploadsManager utility. If the object had been defined to have more than one uploaded file associated with it, then a call to $self->get_upload_filename_icon_and_url would be made for each such field. The icon that is returned indicates the file type (derived from the file suffix).

14-19. These lines use the variables previously retrieved to generate HTML that contains those variables. It is, of course, possible to use other approaches to generate the object's "display" HTML. For example, some gizmos use a Template Toolkit template to build their HTML.

In addition to the two methods above, other display methods may be written. The name of any new method must start with the prefix `www" . For example, one could write a method called www_show_details , which would provide a way to show additional fields that you have chosen not to display in your show method. You can then do all of the processing in the www_show_details method, or it can call additional helper methods.

The `www' prefix is required in order to utilize Metadot's `rendering' framework. In Metadotclass.pm , from which all Gizmos are inherited, the handle method listed below prepends `www' to any op passed as a cgi parameter. (In the future, the framework can be extended to support other types of rendering in addition to HTML).

sub handle {
    my $self = shift;
    my $op = shift;
    $self->debug_msg(3, "Portal::handle($op)");
    ## for HTML RENDERING
    my $handler = "www_$op";
    $self->$handler();
}

Therefore, the name of any new display methods, or other external interface methods, that you write for a new gizmo must prepend `www' as well. Below is the www_show method for the Gizmo class, from which all gizmo subclasses are inherited. www_show calls the ` show ' method implemented for the given gizmo subclass.

sub www_show {
  my $self = shift;
  $self->debug_msg(3, "Gizmo::www_show()");
 
  my $iid = $self->defined_or_exit($self->id,"id is missing.");
 
  my $title = $self->name();
  my $content = $self->show();
  $self->print_portal($content);
}

 

5.3.3 Gizmos and Access Control

When adding new www_ methods, there is an additional issue that must be addressed. As described in Section 3, Metadot security is based on the idea of controlling access to the www_ methods-- essentially, the methods that define the gizmo's external, browser API. These methods are grouped into bundles, with one or more operations per bundle. The operations map to methods, and permissions are assigned to bundles. So, a the operation corresponding to a new method, e.g. the show_details operation corresponding to the www_show_details method , would need to be added to either a new or existing operations bundle. If you do not remember to add a new operation to one of the gizmo's bundles, then you will be blocked from calling that operation from the browser interface.

The Operations class stores the association between the operations (or subroutines) and bundles. To get an operations instance, call the get default permissions as a class method on the superclass of your gizmo (GizmoBuilder). That call looks like this:

my $default_permissions = Discussion->SUPER::get_default_permissions;

Once you have the object, you can modify it as described in the documentation of the Operations class. See Gizmo::Discussion for an example of how the GizmoBuilder module's default permissions can be further modified.

Note that both the retrieval and the modification of the operations object take place as class operations.

Once you are finished modifying the Operations object, you need to make it available as a class method call named get_default_permissions , as below. Note that this class method will override the superclass' method of the same name:

sub get_default_permissions {
    return $default_permissions;
}
5.3.4 Overriding Existing GizmoBuilder Methods

In addition to writing your own new methods, such as www_show_details , you can override existing www_ methods which are inherited from GizmoBuilder. Documentation on these methods is found in the documentation of the Gizmo and GizmoBuilder modules.

These methods are:

    www_save();
    www_delfile();
    www_delfileok ();
    www_show ;
    www_change_owner ;
    www_edit_permissions ;
    www_modify ([$errormsg]);
    www_save ;
    www_delete ;
    www_delete_ok ;
    www_up ;
    www_down ;
    www_cut ;
    www_paste ;
    www_download ;

5.4 Making new Gizmos available to the system

All new Gizmos must be put in the Gizmo subdirectory; this will allow the system to automatically identify them. Any new gizmos will by default appear in the "Manage.." menu listing that appears for a user when editing is enable. If a gizmo of the new Gizmo type is created for a category, it will appear in the center "content" panel for that category (where Tables, Discussions, and Items also appear). The content is grouped by Gizmo type; that is, all Items are displayed together; all Discussions are displayed together, etc.

To have more extensive control over where gizmos are placed on a page, the use of templates and gizmotags is required. See Section 8 below for more information.