We've rebuilt the We have tried to write short, well-named and highly cohesive procs that
never use upvar or uplevel. We have tried to keep site navigation
and excursion handling on the pages. It is very difficult to figure out what
is going on with navigation if it's buried in a proc somewhere. The idea is
that when you look at a page, you see what variables are coming in, you see
the procs that deal with the data machinery and display, you see how the page
handles the navigation from the page. See below about paths to follow the
navigation mechanism. The procs are strictly sorted into their own
libraries. A proc found in km-display will
never retrieve data from oracle. A proc in km-object-data does not
display HTML.
km-defs.tcl has library core-procs having to do with metadata, building
form fields, object access
control, checking user input and excursions (aka paths).
There is one main proc responsible for getting metadata-
km_get_questions. km_get_questions returns all visible question_ids and
various other metadata attributes for any given object type as a keyed list of
lists. km_get_question returns one question and is usually used to return the
next or previous question in the question list by calling it with the
"-next_p" or "-previous_p" options.
The other important metadata procs are km_get_all_object_types and
km_object_type_attributes, which return useful stuff about the object types.
The latter is used on almost every single page in the
km_static caches information about objects whose value is more or
less static throughout the system's life time. The first time a
particular information is being looked for, this information is being
loaded from the database into the cache. km_static does _not_ do any
error checking at the moment. It is also not the smartest caching
system under the sun (not yet at least):
km_conn works very much like ad_conn: it caches information on a library
object on a per-request basis. Put everything that could be a of interest for
an object here and retrieve it from this function. Most of the km_conn cache
is set in km_check_object_id.
km_get_form builds a form of questions for any object type. It takes the
questions from km_get_questions and presents each according to its
There are various display procs that are named after the abstract data types
that are used for them, i.e. km_user_link_field, km_option_field. Also
important is km_get_form_field, a display proc that knows how to call procs
that produce HTML tags from the metadata presentation types. EVERY form in
the
Only small parts of the library have been templated yet, both because we were
working under heavy timeload and because of km_get_form's complexity.
The center proc in the family of user-input procs is km_check_input. It takes
a list of keys, a list of values and an object type and determines if there
has been insufficent or invalid data entered as values. It does this by
calling km_required_questions and then compares a list of required fields for
this object type with the keys and values.
Important here is the proc km_sort_form, which takes all the various
key-value pairs coming in from a input form and assembles them into meaningful
lists according to the abstract data type. So, for example, a question having
abstract data type text will have a sibling variable "html_p" that is sorted
by this procedure into a value tuple, where the first value is the text itself
and the second is the boolean value. Mulitiple-select variables are also
sorted into lists matched to one key. Also: km_assemble_dates. This proc
takes n variables of the triple form var:year var:month var:day and assembles
them to the variable var, whose value is "YY-MM-DD". (Similar to the
date_widget but more flexible for the method of dynamic form generation
required in this system.) var is run through the proc date_p to make sure that
this assembled thing is really a date.
If check_input finds any reason to complain, it returns a a tuple: the
number of complaints and the message strings. These can then be easily
formatted to ad_return_complaint on the page.
This is done by km_create_object.
The central proc in the family of procs that save object data is called
km_save_object_data. It takes a list of keys, a list of values, a user_id and
an object_id as arguments. It saves all data of whatever abstract data type if
possible.
km_save_object_data actually doesn't save any data itself, it calls procs to
deal with the data by abstract data type.
This proc only saves object data of other abstract data types for an object_id
that already exists. (it doesn't make sense to categorize or link an object
that doesn't exist.)
In principle, data is retrieved in the same way it is saved: divided into
abstract data types and then picked out accordingly. The proc that handles
this is known as km_get_object_data. It only needs an object_id and it returns
a key list of lists. Check out the form of the return lists in the proc
itself. It returns everything you need to display this data (but this proc
does NOT display data.) If you only want the data on a few questions or even
just one question, then pass a list of just these questions as an optional
argument. Otherwise it gets data for every single question that can be asked
about this object.
Each question supplied to km_get_object_data is looped through and answered
according to abstract data type. The answer is retrieved by calling a proc
that know how to get this particular type of data. Many of the other procs
that km_get_object_data calls are also in km-object-data.tcl:
km_get_linked_objects gets all data about linked objects for a question of
abstract data type "object_link". It is found in
km_links.tcl. (km_get_child_objects is actually in km-links.tcl, too, since
"child_object" a kind of link is.
The km8_button_panel returns datasources for the 5 buttons that appear as a
navigation panel at the bottom of one-question-edit.tcl. They allow the user
to go to a lot of places that can only be determined by analysing the name of
the submit button. Accordingly, we have procs that create submit buttons of
the style "my_submit_button_name:some_important_number".
Related to these procs is km-defs' km_button_list, which returns a list of
all possible submit button names in
There are also some special little procs that do date stuff. For example,
km_break_date ....breaks a date into 3 parts. Or km_empty_date_p determines if
a date has any empty fields at all. There are some default date procs in
km-defs.tcl.: km_default_day, km_today, km_default_year, km_default_month. The
special proc for
Branches are not abstract data types! Branches are how we call
answer-dependent question sequences in the library. For instance, you have a
question "Who did you vote for?". If the answer was "George W. Bush", then
the following question might be "What reasons do you have to believe that the
Republican party will help you personally?" If your answer was "Al Gore", then
the following question might be "Did you vote Democratic because you were
pleased with the present government?"
Refer to the km-library document
to grasp the finer points of the question hierarchies for branches. The most
important procs are km_get_root_branch, which returns the highest node of the
branch hierarchy for given question_id. Similarly, km_root_branch_p determines
if this is a question is at all a branch node that has children. Important
here is the concept of "active branches". An active branch is one where all
the questions are descendents of an answer that has been chosen higher up in
the branch tree. If later, one answer higher up is changed, then those
questions and answers below it are no longer considered "active" and will not
be displayed in the interface. This is why km_active_branch_p requires the
root_question_id of a branch AND the object_id. Each object will have its own
specific version of the active branch path. The most critical proc is
km_next_branch. Alter with care!!!! It takes a question_id and an answer and,
analysing them according to the abstract data type, determines which question
should follow. It uses the default branch if it can't determine what it should
return, or empty, if there are no children. This proc is used when navigating
one-question-edit.tcl form "previous" question to "next". Make sure you look
at the km8_button_panel in km-display.tcl that predetermines the previous and
next questions when displaying one-question-edit.tcl. And also,
km_get_question in km-defs.tcl, which does NO branch analysis but works
together with km_next_branch in one-question-edit-2.tcl to determine what the
next question will be.
In contrast to These versatile procs should be used on all pages to which an
object or type ID may be passed. They check if the object or type
exists, has been deleted, is readable or writeable by the given user,
and also can print error messages. Note that
km_check_object_id also checks permissions for the object
type. Apart from that, there are also procs for checking if a given user
is a library administrator, may change the owner of an object and may
read or modify an object or object type. A couple of display procs used by browse-one-type,
browse-one-category and the object-view family of pages can be found
here.
The only interesting proc is km_output_object_list, which
is used at a couple of places to output lists of objects (name,
overview, author), possibly with Link buttons when the user is on an
excursion. It checks the access permissions in its own way, since for
speed reasons we have to use SQL joins and cannot use the Tcl procs
defined in km-access.
All the stuff related to categories resides here. We use the
standard categories from the ACS, but need some helper procs to
deal with them. When the user browses objects of one type, he can navigate through
the associated category tree. This proc builds a subtree starting at a
given question or category, and displays it as a HTML table. The count
of objects below each category is passed in as a parameter. Returns all the categories associated with the given question as a
list of tuples holding the category_id and the indented category
name. This is used for data entry, where we want to display an
indented list in a selection box. Counts how many objects there are for a category and its
subcategories. The result can then be used for km_category_table. Mapping between objects and categories and between questions and
category trees; procs for handling category trees (determing the root
category, e.g.); small display procs.
Editing and display of general comments and reuse feedback for
knowledge objects. Uses the site-wide service general-feedback.
Procs used to display the users Workspace (saved items), and
his own objects (with some information about their current state). This is
also the place for hooking into ACS' system of listing user contributions and
new stuff (km_contributions).
km-categories.tcl holds procs that have to do with
categories and some of these do display something. (these procs are part of
km_get_answer_options, km_get_user_links.
km_get_categories gets all category
data for a particular question of abstract data type "categorization". It is
found in km_categories.tcl.
sarnold@arsdigita.com