About DynFields

Original Text by Frank Bergman and Juanjo Ruiz

What is DynField?

DynField is an extension of the original OpenACS SQL Metadata system. DynField allows developers to extend objects with new fields. These new fields will appear automatically on all suitable OpenACS pages that deal with this object such as the object's view, edit and list pages.

DynField is an _extension_ mechanism which allows adding dynamic attributes to existing OpenACS objects. However, DynField is not a generic object management system. The creation and deleting of objects are outside of the scope of DynField, because is functionality is frequently handled using PL/SQL or PG/SQL in an object specific way.

DynField is based on code of the great Attribute Management System (AMS) from Matthew Geddard and Richard Hamilton. Also, this document is based on their documentation.

Do I need DynField?

The main purpose of DynField is to allow developers to customize the system for specific web sites or customers without modifying the underlying TCL and ADP source code in the CVS version control system.

  • You probably won't need DynField if you only develop a single web community or if you are in the business of adapting OpenACS to customer requirements.
  • However, you probably need DynField if you develop a new module that may be useful to many customers with slight variations for each customer.

Design Decisions and Requirements

Please see the The Need for a Different Approach to DB Extendibility for an overview over design decisions and requirements. Basicly, we tried to keep the system as simple as possible (it only defines two new tables) and to stick as much as possible to the original OpenACS Metadata system. Also check the Slides on Dynfield Extensible Architecture.

Architecture

The architecture consists of an extension table to acs_attributes and one new table containing pre-configured "widgets":

acs_attributes already contains the link to OpenACS objects via the object_type field, so dynfield_attributes is basically an extension table that links to dynfield_widgets.

DynField and ad_form

You have two options when dealing with DynField and ad_form. Shorthand and detailed.

Shorthand

Shorthands is a completely simple way of creating forms without many options. The supplied object_id must already exist in the acs_object table. The shorthand procs is dynfield_form, which is simply a wrapper for ad_form. For example, to create and ad_form named "contact_person_ae" create a page contacts/www/contact-person-ae.tcl with the following content:

 ad_page_contract {
 } {
        {ct_contact_id:integer,notnull}
 }
 set title "Contact Person Add/Edit"
 set context [list $title]
 dynfield_form -package_key "contacts" \
         -object_type "ct_contact" \
         -page_name "edit_page" \
         -form_name "contact_person_ae" \
         -object_id $ct_contact_id \
         -return_url ""
 ad_return_template

The positioning of the form elements on the screen is defined with the attributes themselves. Please see the DynField "layout manager" for details.

The contacts/www/contact-person-ae.adp would contain:

 <master>
 <property name="title">@title@</property">
 <property name="context">@context@</property">
 <formtemplate id="contact_person_ae">
 </formtemplate">

That's it. If this isn't flexible enough you can also go with the detailed method.

Detailed

For many application the DynField and ad_form shorthand will be too simplistic. For those situations, you can use DynField to interface with ad_form. You need to define ad_from -form elements like this:

 ad_form ... -form [dynfield::ad_form::elements \
		-package_key "contacts" \
		-object_type "ct_contact" \
		-list_name "contact_person"] ...

Note that this procedure returns an ad form appropriate element list. If you intending to define other elements you will need to ad_from -extend -name form_name -form ...

In the ad_form -edit_request block put

 ad_form ... -edit_request {
        dynfield::object::attribute::values -vars -object_id $object_id
    } ...

This returns the variables upvared into your page, i.e. the first_names attribute could be returned with a value of "Jane" and the last_name attribute with a value of "Doe"... etc. ad_from looks for all form elements and appropriately pre-fills the form with the given values.

In the -on_submit block you enter the following:

 ad_from ... -on_submit {
        dynfield::ad_form::save \
            -package_key "contacts" \
            -object_type "ct_contact" \
            -list_name "contact_person" \
            -form_name "contact_person_ae" \
            -object_id $ct_contact_id
    }

This is how you interface with DynField and ad_form. You may also specify other code in the -form -on_submit and -on_submit blocks.

DynField and Your Package's UI

To display attributes you can call dynfield::object::attribute::values to get the results back as upvared variables, as an array or as a list however you want. So, if on the contact-view page you do, for example to get an array with all attribute_values that are not null represented in.

dynfield::object::attribute::values -array "contact_info" -object_id $ct_contact_id

To add dynfield_attribute_values to a multirow you call dynfield::multirow::extend to efficiently extend your multirow with dynfield_attribute_values. For example:

db_multirow contacts get_contacts { select ct_contact_id from ct_contacts }
 dynfield::multirow::extend \
    -package_key "contacts" \
    -object_type "ct_contact" \
    -list_name "contact_person" \
    -multirow "contacts" \
    -key "ct_contact_id"

 

"Pages" and Attribute Layout

DynField defines the notion of a "page", allowing attributes to be layouted differently in different TCL/ADP pages. Each page defines the position for each attribute. There are three different types of layout defined in DynField, for each type there are specific actions to be taken, but in all of them you can specify which attributes you want to show and which not. This combined with the Cascade Style Sheets and the style option in the <formtemplate> ACS templating tag gives a wide variety of formating options.

Hint: The layout management makes heavy use of the acs-templating and template::form systems, if you do not feel comfortable with it, please revise the corresponding documentation.

  1. "Absolute positioning"
  2. The "Absolute positioning" uses DIV classes for each label/widget pair. Once the classes are defined is up to the CSS to move each field to the desired place. The use of DIV tags complies with European Accessability regulations.

  3. "Relative positioning"
  4. The "Relative positioning" uses the same principle as the <grid> tag of the ACS template system in order to emulate a table-like structure.

    First we define the height and the width of the desired table and after that we sort all the included attributes. The table-builder will use this sort order and the width (x) of the table, so the first (x) elements will be placed in the first rown, the second ones in the second rown, and so on as you can see in the next table example:

    1st 2nd 3rd
    4th 5th 6th
    7th 8th ...

  5. "ADP template"
  6. "ADP template" has all the posible freedom because you are editing line by line how you want your form to look like. For the people who has already embebed some dynamic form in a template it should be pretty straight forward as they are used to use the different <form[...]> tags. The dynfield package just adds two new tags to the existent ones which we think are very usefull:

    • <formlabel>: Returns the label of the element
    • <formhelptext>: Returns the help text and the 'chunk' between the (<>) and (</>) tag
    For those who have never work with formtemplate there is an example.

Layout process

The dynfield package is just an extension of what we already have in our page. So you can add fields to pages without form, pages defining the form in the adp file or pages using template::form/ad_form. The drawback of this implementation is that one can get confuse about what is really being executed at one moment and which what values. The standard process shall be the following:

  • Call to the load&show dynfield procedure in the tcl file which:
    1. Get the old attributes values from the database
    2. Set the page layout/style. First look at the -style paramenter, if nil use the -page_url parameter, if nil get the default page_url for the specified object_type, if nil not to use layout manager (i.e. use standard ACS layout)
    3. If page_url is used get the layout variables of the corresponding layout_type
    4. Create form and form elements
  • Call to the <formtemplate> tag in the adp file
  • Call to the store dynfield procedure in the 'is_valid' section of your template::form/ad_form or in the *-2.tcl page if you do not use it.

Layout tables

The layout pages are defined in the table dynfield_layout_pages:

  • object_type: each page can use attributes from more than one object_type
  • page_url: we use the global page url as the page identifier
  • layout_type: one of the previusly described types 'absolute' 'relative' 'adp'
  • table_width: number of rows for the table, only for 'relative' layouts
  • table_height: number of cols for the table, only for 'relative' layouts
  • adp_file: name of the adp template to use, only for 'adp' layouts
  • default_p: use this page as the default one for this object_type

The dynfield_layout table define the attributes showed in each page

  • attribute_id: dynfield attribute identifier
  • object_type
  • page_url: reference to dynfield_layout_pages
  • class: the name of the CSS class, only for 'absolute' layouts
  • sort_key: an integer defining the place of the attribute, only for 'relative' layouts

... For further reference please look the documentation in the dynfield procedures.

Extension Tables

The figure to the right shows a typical setting for the object type "User". "User" information is generally stored in the "users" table. However, part of the information is stored in the superclasses of "User" such as "Person", "Party" and "Object".

As a consequence we will have to gather information from all of these "extension tables" when displaying a "User" and we will have to save information to all of these tables when modifying a user. The management of these extension tables can be very difficult in practice, because there can be any number of database constraints related to these extension tables, and some of the extension tables may require a creation using a PL/SQL or PG/SQL database procedure.

This is the reason why we decided NOT to support a generic object creation functionality in DynField. Instead, DynField restricts itself to modifying existing objects, which can be mapped into SQL "update" statements for the extension tables. Even with these restrictions it is possible that a DynField "update" will violate database constraints. However, the probability is much lower in general.

 

DynField Widgets

DynField defines several kinds of widgets:
  • Standard OpenACS Widgets:
    These widgets use the existing OpenACS widgets to display a wide range of data types.
    DynField sets up a number of these widgets during the installation process, so they are ready to use
  • Category Select Widgets:
    These widgets define drop-down boxes with options defined using the OpenACS categories module.
    Please read below how to create a new Category Widget.
  • SQL Select Widgets:
    These widgets define drop-down boxes using an SQL statement to determine the range of valid options.

The following section explains the different choices for a widget in detail:

 

Storage Type

The storage type determines how the value of the widget is stored in the database. There are several options:

  • "Table Column":
    The standard storage type: A new column is created in the object's table or in an extension table to store the attribute value.
  • "Multi-Select Mapping Table": (not implemented yet!)
    A special storage type to store multiple values, for example from a multiple select box. The values are stored using an internal mapping table. ToDo: Implement and describe mapping table.

OpenACS Datatype

The OpenACS datatype is a high-level description of the datatype. It is used to create both the SQL datatype for Oracle/Postgres and the datatype for the ad_form.

OpenACS Datatype SQL Datatype
string varchar(1000)

boolean

char(1)
number "number"
money number (12,2)
date date
text varchar(4000)
integer integer

enumeration

varchar(100)
keyword varchar(1000)

 


Slides on Dynfield Extensible Architecture

  Contact Us
  Project Open Business Solutions S.L.

Calle Aprestadora 19, 12o-2a

08902 Hospitalet de Llobregat (Barcelona)

Spain

 Tel Europe: +34 609 953 751
 Tel US: +1 415 200 2465
 Mail: info@project-open.com