]project-open[ : @This Wiki
Portrait

Welcome, Unregistered Visitor

 · · · Index · Login/Register

Contents

5 registered users
 in last 24 hours

DynField - Extensible Architecture

Error in Page en:dynfield_extensible_architecture: can't read "context": no such variable

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 initial posting at OpenACS 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 enclosed PowerPoint slides.

Architecture

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

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

Defining New Attributes

DynField comes with several maintenance screens that allow to define new attributes for a given object type. There are two options:

  • Create a new entry for an attribute that already exists.
    Use this option in order to enter the existing object fields into DynField. This allows you to use DynField to create object maintenance screens
  • Create a new entry and create a new table column.
    Use this option if you really want to extend the object type.

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.

Attribute Widgets

The most important information of an attribute is defined via its "Widget". This widget includes the definition of:

  • How the attribute is displayed in an HTML form (using a text box, a drop-down box etc.)
  • How it is stored in the database
  • Whether or not a validator can be applied to the attribute (when editing the attribute value)

In short, the widget defines the semantics of an attribute. Widgets are explained further below.

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"

ToDo ...

 

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

 

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)

Datatype

 

Widget

 

 

Creating a New Category Widget

Creating a new Category Widget requires an integration between the OpenACS "Categories" package and DynField:

  1. You have to create a new Category tree for the widget (if it doesn't exist already). Go to the category admin pages at /categories/cadmin/ and select "Create a new tree". Let's call it "Customer Classification" and press OK.
  2. Select the new tree and choose "Add node at top level" to give it its root node. Let's call the root node "No Classification" and press OK
  3. Now you can successifly add sub-categories to "No Classification" by clicking on the "Add child" link. For example we might add the sub classifications "A" (= great customer), "B" (= ok customer) and "C" (= customer to get rid of)
  4. Finally on the category part, we have to find out the "tree_id" of the current tree. To do this, please check the current URL for a "tree_id" piece, which is followed by a number. Perhaps "tree_id" is written as "tree%5fid=630", with the "%5f" representing the underscore "_". However, we need the number behind the "=", so the 630 in this example.
  5. Now let's go to /dynfield/widgets/ and select "Create a new widget" link at the bottom.
  6. Let's enter the following data:
    • Widget Name = "customer_classification"
    • Pretty Name = "Customer Classification"
    • Pretty Plural = "Customer Classifications"
    • Storage Type = "Normal Value"
    • ACS Datatype = "Integer"
    • Widget = "Category"
    • Datatype = "Integer" and
    • Paremeters = "{custom {category_tree_id 630}}" (please replace the 630 by your tree_id)
  7. That's it. Now you have a new "customer_classification" widget that is available when you define new attributes of an object.

 

DynField Permissions

ToDo

Possible values are

  • absolute_links: '0'
  • abstract_p: 'f'
  • auto_save: 'false'
  • creation_date: '2009-04-08 17:10:32.27804+02'
  • creation_user: '272160'
  • creator: 'Richard Thiemann'
  • current_user: '0'
  • description: 'An explanation of the DynField Design Architecture'
  • do_substitutions: '1'
  • folder_id: '-100'
  • form: '::xowiki::WikiForm'
  • id_column: 'page_id'
  • last_modified: '2009-04-08 17:10:32.27804+02'
  • mime_type: 'text/html'
  • modifying_user: '272160'
  • name: 'en:dynfield_extensible_architecture'
  • nls_language: 'en_US'
  • object_id: '274757'
  • object_type: '::xowiki::Page'
  • package_id: '252723'
  • page_id: '274757'
  • page_order: 'Dynfield'
  • parent_id: '252746'
  • pretty_name: 'XoWiki Page'
  • pretty_plural: 'XoWiki Pages'
  • publish_date: '2009-04-08 17:10:32.27804+02'
  • publish_status: 'ready'
  • recursion_count: '0'
  • references: ''
  • render_adp: '1'
  • revision_id: '274757'
  • security_inherit_p: 't'
  • sql_package_name: '::xowiki::Page'
  • storage_type: 'text'
  • supertype: 'content_revision'
  • table_name: 'xowiki_page'
  • title: 'DynField - Extensible Architecture'
  • unresolved_references: '0'
  • with_table: 'true'




Please take a moment to complete this form to help us improve our service.

Did this page help you to achieve your goal?

 Yes  No  Don't know

Please provide us with comments to improve this page:

How useful is the information?

 1  2  3  4  5
Not
useful
      Extremely
useful