Building Screens using the KNS Tag Libraries

The Kuali Nervous System handles the rendering of several pieces of standard functionality: maintenance documents, inquiry pages, and lookups. However, that leaves two pieces of functionality where writing JSP is required: on transactional documents and on non-document screens. However, even though JSP coding is required in these cases, the KNS still provides a wealth of rendering functionality through the use of tag libraries.

This section examines several categories of the most used tags that are provided by the KNS.

Implicit Variables

The KNS provides a number of implicit variables which can be used in the context of JSP pages. These variables exist to give the web layer the ability to read variables from the other KNS layers.

For instance, the variable Constants is used to give web layer developers access to org.kuali.rice.kns.util.KNSConstants, as so:

<c:if test="${KualiForm.documentActions[Constants.KUALI_ACTION_CAN_EDIT]}">
    Howdy, end user!  You can edit this page!
</c:if>

Client applications often overload this variable to hold not only KNS constants but application specific constants as well. There is also a RiceConstants variable which holds the constants in org.kuali.rice.core.api.util.RiceConstants, a KewApiConstants which holds all of the constants in org.kuali.rice.kew.util.KewApiConstants, and a PropertyConstants which holds the values in org.kuali.rice.kns.util.KNSPropertyConstants.

All configuration properties are loaded into a variable ConfigProperties

<p>
application namespace is <c:out value="${ConfigProperties['config.namespace']}" />
</p>

The entirety of the data dictionary as also exposed in the map constant DataDictionary. The keys for this map are either the simple class name of a business object:

<c:set var="countryBODataDictionaryEntry" value="${DataDictionary['CountryImpl']}" />

Or, for documents, the KEW document type name:

<c:set var="identityManagementPersonDocumentEntry" value="${DataDictionary['IdentityManagementPersonDocument']}" />

Data dictionary values can then be accessed via JSP EL.

The final implicit variable to mention is KualiForm. This is the Struts form for the current page. For JSP pages supporting transactional documents, values from the document can be read through KualiForm. As such, this implicit variable is practically the most used.

These implicit variables work together to support the various tags the KNS provides.

Tags for Layout

KNS applications have a standard look, and client application developers will want to preserve that look. KNS layout tags provide an easy way to use the KNS look and feel.

First of all, JSP pages using tag libraries need to have @taglib directives added to the page:

The KNS tag library is typically imported with the kul: prefix:

Thankfully, a collection of common JSTL, Struts, and Rice tags are readily imported using a single import at the top of any custom developed JSP page:

Having done that, the developer can use the kul:page tag to draw the main outline of the page, such as in this example, from KFS’s Format Disbursements page (formatselection.jsp):

The kul:page simply draws the frame around the page. It has two required attributes: the docTitle, which is the title it will use for the page in the gray bar which runs along the top, and the transactionalDocument attribute, which should only be true if the JSP page is supporting a transactional document.

This example uses a number of other attributes as well. The headerTitle is what will show in the browser’s title bar. showDocumentInfo will treat the page as a document page and will attempt to, for instance, show a link for document type. The errorKey is the key for errors which should be associated with the top level of the page (in this case, it likely should have been neglected). Finally, the htmlFormAction is the url to the action that the form within the page – every page is assumed to have HTML form data, so an HTML form variable is constructed for it – should post to.

There’s also a convenience tag that encapsulates the kul:page with all of the attributes needed for documents turned on: the aptly named kul:documentPage, exemplified here from the sample travel app:

The only required attribute here is documentTypeName; the docTitle will display the label from the data dictionary entry associated with this document, and the value for the transactionalDocument attribute will also be determined from the data dictionary entry. All other attributes will simply be passed along to the kul:page tag.

The kul:documentPage tag makes sure that not only is the document title splashed across at the top of the page with a light gray, scrubbed looking background graphic, but also shows common read-only document information: document number, KEW workflow status, initiator, and when the document was created.

The next most distinctive visual feature of KNS pages are the tabs which visually organize related information (through headers, it organizes the information for sight disabled end users as well). The KNS provides a main tag to draw these: not surprisingly, it’s the kul:tab tag.

Here is an example of the tag, again from travelDocument2.jsp, which is part of the sample travel application:

There is only one required attribute for the tab: defaultOpen, which declares whether the tab should be initially rendered as open or closed (all tabs can be opened or closed once rendered). However, this example gives us a number of other useful attributes as well. tabTitle is the name that will appear in the tab; while not required, best practice suggests that developers provide one so the tab have a label even when closed. tabErrorKey lists the keys that will be associated with this tab; when those errors are rendered, their messages will be associated with the given tab.

Another thing to notice in the example was the inclusion of a div with class “tab-container”. In practice, practically all KNS tabs have such a tab included. This leads to the natural question of why the tab is not part of the tag itself.

The div with a class of “h2-container” draws a header stripe at the top of the tab, with a black background and white text. This distinctive visual element should be used to mark off sub-sections of the tab.

There is also a kul:subtab. This visually provides an in-set tab, typically set off with a stripe that has a gray background and bolded black text. KIM’s Identity Management Person Document, has such an example. It includes the tag personContact.tag, which builds a tab:

This splits the various sub-sections into distinctive visual elements.

The only required attribute for the tag is width, which specifies the width of the sub-element (kul:subtab’s are sometimes shorter than their surrounding tab – while they are always rendered with some padding, the amount of padding and thus the amount of visual separation can be increased as width is decreased).

Sub tabs often have titles, specified through the subTabTitle attribute. Whereas all tabs have hide/show buttons, they can be turned off from sub tabs through the use of the noShowHideButton attribute.

Finally, sub tabs are often associated with lookups, they have two attributes, lookedUpBODisplayName and lookedUpCollectionName, which allow results of lookups to be displayed in the sub tab itself.

Astute readers will have noticed an important visual point about tabs: the tab is rendered with the tab title in an offset visual element (like the tab in a file folder) and behind it is the gray background of the tab above. However, the top tab does not have a tab above it. Therefore, for that special top tab, there is a kul:tabTop which is identical to the kul:tab tag, save that it visually looks like the top tab. Also, to round off the bottom tab, there is a tag, kul:panelFooter, which takes no attributes, which will round off the bottom corners of the set of tabs.

It should be noted that for documents, general practice is that the top tab provides the standard set of fields that all KNS documents have: the document description, which is a required field, as well as a text area for the document explanation and an internal Org Doc #. Since this is standard, the KNS provides a tag, kul:documentOverview, which displays these fields and which is commonly the top tab of the document (thus obviating the need for the developer to use the kul:tabTop tag).

Practically all documents will share this line of code as the top tab. The editingMode attribute is required, but will practically always be the value of KualiForm.editingMode.

Armed with these visual layout tags, client application developers are ready to start filling in pages with form controls.

Tags for Controls

Certainly, controls can be hard coded in JSP files as HTML. However, the KNS provides several tags which provide standard functionality to controls – thus preserving the flexibility of declaring control information in the data dictionary as well as supporting masking, accessibility, and a number of other concerns without the application developer needing to concern with those details.

The basic tag for showing a field is kul:htmlControlAttribute. Dozens of examples can be found in even the simplest Rice client application. Here is the tag being used in travelDocument2.jsp in the Rice sample travel application:

<table width="100%" border="0" cellpadding="0" cellspacing="0" class="datatable">
    <tr>
        <kul:htmlAttributeHeaderCell labelFor="document.traveler" attributeEntry="${travelAttributes.traveler}" align="left" />
        <td><kul:htmlControlAttribute property="document.traveler" attributeEntry="${travelAttributes.traveler}" readOnly="${readOnly}" /></td>
    </tr>
    <tr>
        <kul:htmlAttributeHeaderCell labelFor="document.origin" attributeEntry="${travelAttributes.origin}" align="left" />
        <td><kul:htmlControlAttribute property="document.origin" attributeEntry="${travelAttributes.origin}" readOnly="${readOnly}" /></td>
    </tr>
    ...
</table>

This example has two controls which will appear on the form: one for document.traveler and one for document.origin. This is set via the property attribute; that attribute is required. Also required is the attributeEntry attribute, which takes in the DataDictionary attribute entry for the attribute that is being displayed:

<c:set var="travelAttributes" value="${DataDictionary.TravelRequest.attributes}" />

There are also many optional attributes. One is seen in both examples above: readOnly, which determines if the field will simply have a read only version of its value displayed, or a control will be displayed. This attribute allows a lot of flexibility about when a field will be readOnly or not. Typically, though, readOnly is determined based on the whether there’s an action “can edit” in the form’s documentActions map:

<c:set var="readOnly" value="${!KualiForm.documentActions[Constants.KUALI_ACTION_CAN_EDIT]}" />

As covered earlier, masking is handled automatically if the field is read only. If the value of the property should not be displayed at all, the attribute readOnlyBody can be set to true and the value of the tag’s body is displayed if the control attribute is rendered read only.

Among the other optional attributes are html attributes which are applied directly to the drawn control, such as onblur, onclick, and onchange. There is a styleClass, which is where a CSS class can be specified to render the value or control in.

Note that the type of control is not specified here. The data dictionary entry will be referred to, and that control definition will used to determine which control will be rendered. Select controls will use a values finder to find the values to display in the drop down. This means that controls can be changed without altering the JSP, which is a major strength.

The only exception to be aware of is that if a text control document contains a date, there is an attribute, datePicker, should be set to true.

Also in the example, the tag kul:htmlAttributeHeaderCell is used. It displays the label for the field in a <td> cell. There aren’t officially any required attributes, though one of the following three would have a value set: attributeEntry, attributeEntryName, and literalLabel. literalLabel will force the header cell to simply display the given String. attributeEntry, on the other hand, will use a data dictionary attribute to find an appropriate label; it needs to be handed the proper label much as the kul:htmlControlAttribute uses. attributeEntryName takes the full name of a data dictionary attribute (such as “DataDictionary.TravelRequest.attributes.origin”). The label will come from the data dictionary, though the tag will do all of the lookup itself.

There are a number of other attributes exist which control how the html of the <td> tag will render. width, rowspan, colspan, align, and labelFor, as well as several others exist to customize the look of the tag.

What if a label is being rendered outside a table? For that, there is a kul:htmlAttributeLabel tag. It allows attributeEntry and attributeEntryName attributes which work just as they do in kul:htmlAttributeHeaderCell. literalLabel is not supported (since it is assumed that a literal label would simply be written into the JSP).

This too has a number of other attributes. Developers should consider three of these attributes. useShortLabel uses the short label in the data dictionary attribute instead of the regular label. noColon is a boolean. If it is set to true, then there will not be a colon rendered after the label. Finally, forceRequired means that a symbol will let end users know that the field is required.

There is also a convenience tag which belongs on every JSP page supporting a transactional tag, right after the kul:documentPage tag: kul:hiddenDocumentFields. Here is its use in travelDocument2.jsp:

<kul:documentPage showDocumentInfo="true" htmlFormAction="travelDocument2"
 documentTypeName="TravelRequest" renderMultipart="true" showTabButtons="true" auditCount="0">
<kul:hiddenDocumentFields />

This will make sure that the docId and document.documetNumber will be preserved to repopulate the form after an action occurs on the document by creating HTML hidden controls to carry the values through the POST.

There are two optional attributes, used to ask for the saving of more variables. If includeDocumentHeaderFields has a value of true, it will make sure that document.documentHeader.documentNumber is saved. Setting includeEditMode will preserve the edit modes determined for the document.

Finally, kul:errors should be mentioned. As previously seen, errors are typically associated with pages and tabs via errorKeys. If an error should show up not associated with a page or a tab but rather with some other visual element, then the kul:errors tab can display those.

There are no required attributes. If only the errors with a certain set of keys should be displayed, then the keyMatch attribute should be set. Otherwise, all remaining messages will be rendered. Forcing the rendering of all remaining messages can be forced by setting the displayRemaining attribute to true. An errorTitle, warningTitle, and infoTitle can also be set to separate the message sections. Defaults are provided if these attributes are not set.

Tags for KNS Functionality

Developers of transactional documents or screens will often want to hook into KNS functionality, such as inquiries and lookups. A set of tags makes this easily accomplished.

For instance, in Rice client applications, many controls have a question mark icon next to them, which allows the user to do a lookup and return the value into the control. To get one of those to display, the kul:lookup tag must be utilized, precisely as it is on travelRequest2.jsp:

<kul:htmlControlAttribute property="travelAccount.number" attributeEntry="${accountAttributes.number}" readOnly="${readOnly}" />
<kul:lookup boClassName="edu.sampleu.travel.bo.TravelAccount" fieldConversions="number:travelAccount.number" />
<kul:directInquiry boClassName="edu.sampleu.travel.bo.TravelAccount" inquiryParameters="travelAccount.number:number" />

Right after the travelAccount.number control is rendered, the kul:lookup tag will render the question mark lookup icon.

It takes the class of the business object it will perform a lookup against through the required boClassName attribute. The fieldConversions attribute is not strictly required but often used: it is a list of attributes from the result business object matched by a colon with the field that it should populate in the document upon return. kul:lookup also has support for a lookupParameters tag, which will populate the lookup with values from the document. There are a number of other optional attributes as well.

Also in this example is the kul:directInquiry tag. If the travelAccount.number field is filled in, then clicking the directInquiry tag will open up an inquiry page for the value given.

It, too, needs the class of the business object it is inquiring on through the required boClassName attribute. The non-required inquiryParameter attribute tells the tag which values to take from the document to use as keys for the inquiry page.

What if the value is read only and an inquiry should be displayed? In that case, the kul:inquiry tag should be used. Here is an example from the KFS procurementCardTransactions.tag:

<kul:inquiry boClassName="org.kuali.kfs.fp.businessobject.ProcurementCardTransactionDetail" keyValues="documentNumber=${currentTransaction.documentNumber}&financialDocumentTransactionLineNumber=${currentTransaction.financialDocumentTransactionLineNumber}" render="true">
    <bean:write name="KualiForm" property="document.transactionEntries[${ctr}].transactionReferenceNumber" />

</kul:inquiry>

The kul:inquiry works much like the <a> tag it renders. Any text within the body of the tag is rendered as the text for the link. It, too, requires the boClassName attribute which specifies which business object will be rendered on.

It also requires two other attributes, keyValues and render. render is an odd attribute. It decides whether the inquiry link will be rendered or not. This allows some display level logic to check whether the field should actually be rendered on. If render is false, then only the text of the tag’s body will be rendered.

keyValues hands in the query string to pass to the inquiry page, theoretically with the keys the inquiry page will need to find the record to display.

kul:inquiry has no optional attributes.

A variation of the kul:lookup tag also exists, which supports multiple value lookups, kul:multipleValueLookup. Here is an example from KC’s awardKeywords.tag:

<kul:multipleValueLookup boClassName="org.kuali.kra.bo.ScienceKeyword" lookedUpCollectionName="keywords" anchor="${tabKey}"/>

Once again, boClassName of the business object class to be looked up is a required attribute. Also required is the lookedUpCollectionName attribute. Once the multiple values are returned from the lookup, the KNS will attempt to populate the named collection on the document with the values.

In this example, anchor is an optional attribute. It gives the link to return to an anchor to navigate to when it returns to the page. This is helpful on long pages. There is also an attribute lookedUpBODisplayName which will control the label for the business object being looked up.

Last, but by no means least, among these tags is the reliable kul:documentControls tag. Every JSP supporting a transactional document will include this tag, as it draws the row of controls on the very bottom of the page, thereby allowing end users to route, save, approve, cancel, and otherwise work with the document. travelRequest2.jsp uses it:

<kul:panelFooter />
<kul:documentControls transactionalDocument="false" />

Properly utilized this control appears just beneath the kul:panelFooter. The only required attribute is the transactionalDocument attribute, though, ironically, that attribute is never used within the tag. It therefore does not matter if false or true is entered as the value.

The other main attributes to be aware of support adding extra buttons. There are two mechanisms. In the first, by specifying the extraButtonSource, extraButtonProperty, and extraButtonAlt attributes, a single extra button will be rendered. For the image source, it will use extraButtonSource, with the alternate text specified by extraButtonAlt. The extraButtonProperty specifies the property of action to call (for instance, the property of the route button is “methodToCall.route”).

That’s fine for one extra button, but what if multiple extra buttons need to be added? The KNS supports this as well. org.kuali.rice.kns.web.struts.form.KualiForm has a List property named extraButtons. The List is made up of org.kuali.rice.kns.web.ui.ExtraButton objects. Each ExtraButton object, in turn, has an extraButtonProperty, extraButtonSource, and extraButtonAltText properties which can be set. Those properties have the same effect as the extraButtonSource, extraButtonProperty, and extraButtonAlt attributes covered above. ExtraButton objects have two extra properties as well: extraButtonParams and extraButtonOnclick which provide the ability to hand extra parameters to the action and the ability for javascript to react to the button click respectively.

The form can have its extraButtons list populated before reaching the presentation layer. Most often, this is accomplished by simply overriding the form’s getExtraButtons() method. Then the extra buttons are simply sent from the form into the kul:documentControls tag, as so:

<kul:documentControls transactionalDocument="false" extraButtons="${KualiForm.extraButtons}" />

The kul:documentControls tag will then render all of the extra buttons. Given its extra flexibility, this is the preferred method of adding extra buttons.

Useful Pre-Created Tabs

Finally, the KNS provides a number of tabs that happen to exist on most documents.

For instance, practically every document has the ability to add notes. If that functionality is to be turned off, it’s much easier to do in the data dictionary – so frankly, every document should have a place to enter notes. Documents should also have the route log of the document, and a place where ad hoc KEW recipients can be added. The KNS makes adding all of these tabs easy:

<kul:notes />
<kul:adHocRecipients />
<kul:routeLog />

The names of the tags are self-explanatory; and as easy as that, these three standard tabs have been added to the document.