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.
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.
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.
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
.
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.
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:
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'));
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.
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. }
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);
}
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;
}
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.
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 ;
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.