We've created the model - the Form - for our document, and the controller - the Action. Wonderful! That leaves us simply...the view. HTML. div's and CSS's and JavaScripts...oh my. Thankfully, JSP creation is simplified in KFS, just as creating the Struts Form and Action was simplified. Naturally, JSP pages can't inherit from other JSP pages, of course, but they can include other JSP pages and use JSP-defined taglets. Both these strategies ease the JSP creation process considerably.
Creating a JSP page
Let's look at a JSP page for a transactional document. This is InternalBilling.jsp, the JSP page for org.kuali.kfs.fp.document.InternalBillingDocument. It lives in the work/web-root/jsp/modules/financial/ directory.
1 <%@ include file="/jsp/sys/kfsTldHeader.jsp"%> 2 <kul:documentPage showDocumentInfo="true" 3 htmlFormAction="financialInternalBilling" 4 documentTypeName="InternalBillingDocument" renderMultipart="true" 5 showTabButtons="true"> 6 7 <sys:documentOverview editingMode="${KualiForm.editingMode}" /> 8 <kul:tab tabTitle="Accounting Lines" defaultOpen="true" tabErrorKey="${KFSConstants.ACCOUNTING_LINE_ERRORS}"> 9 <sys-java:accountingLines> 10 <sys-java:accountingLineGroup newLinePropertyName="newSourceLine" collectionPropertyName="document.sourceAccountingLines" collectionItemPropertyName="document.sourceAccountingLine" attributeGroupName="source" /> 11 <sys-java:accountingLineGroup newLinePropertyName="newTargetLine" collectionPropertyName="document.targetAccountingLines" collectionItemPropertyName="document.targetAccountingLine" attributeGroupName="target"/> 12 </sys-java:accountingLines> 13 </kul:tab> 14 <fp:items editingMode="${KualiForm.editingMode}" /> 15 16 <c:set var="readOnly" value="${!KualiForm.documentActions[Constants.KUALI_ACTION_CAN_EDIT]}" /> 17 <fp:capitalAssetEditTab readOnly="${readOnly}"/> 18 19 <gl:generalLedgerPendingEntries /> 20 <kul:notes /> 21 <kul:adHocRecipients /> 22 <kul:routeLog /> 23 <kul:panelFooter /> 24 <sys:documentControls 25 transactionalDocument="${documentEntry.transactionalDocument}" extraButtons="${KualiForm.extraButtons}" /> 26 27</kul:documentPage>
Looks semi-painless, right? Let's take a look at each piece.
Line 1 is a JSP include directive, which includes the kfsTldHeader.jsp. kfsTldHeader.jsp basically imports all of our taglibs, and since those are so heavily used in Kuali; including the file is highly convenient, so when we create JSP pages, we'll include that file as well.
Lines 2 and 7 declare the kul:documentPage. As we remember, each JSP tag or taglet has a name space; tags from Kuali Rice use the "kul" namespace and each module in KFS has its own namespace (like, "sys", "gl" and "fin"). The "html" namespace comes from Struts, of course.
Back to the documentPage tag. This is the tag that declares the page. The documentPage tag has a number of attributes, but the most important ones are used here. showDocumentInfo decides if the page should show "document" information, such as help links for the document. htmlFormAction is the name of the KFS Struts mapping that requests should be posted to. documentTypeName is the name of the document, as defined in the data dictionary. renderMulitpart means that the form tag should be a multi-part form, so that, for instance, files could be uploaded to it (since all documents with notes can have file uploads, it's typically wise to set this to true). Finally, showTabButtons either shows or hides the "show" or "hide" buttons on tabs.
sys-java:accountingLines does the magic of painting accounting lines, details should be covered under section Accounting Documents.
Lines 9 - 13 are common to most pages. kul:notes shows the Notes tab and kul:adHocRecipients renders the Ad Hoc Routing tab. kul:panelFooter draws the bottom of the page above the buttons; neglect this and our page will look lame, forcing graphic designers to laugh at us. The kul:documentControls shows the workflow buttons - the submit button, save button, cancel button - whatever buttons should be turned on for our document.
As we can see, using tag libraries saves us a lot of effort. Therefore, we're going to take a look at commonly used tags, and then explore how to create a tag ourself.
Tags to know
Here's a list of helpful tags to know when we create our own JSP pages or taglets:
- kul:documentPage - if our page is a view for a document, then we want to use the kul:documentPage tag to set up the page for us; this page acts a wrapper for all the other tags within our document page
- kul:page - likewise, if our page is not a view for a specific document, then we want to use the kul:page tag. This tag has many more attributes so that it is more flexible, but most use of it will look a lot like kul:documentPage
- sys:documentOverview - this draws the header, which is to say the document title and the upper right hand block of information including document number and initiator, for the given document
- kul:panelFooter - again, this draws the bottom curve on a page just before the buttons. It makes our pages look nice.
- kul:documentControls - these are the traditional buttons for a document, turned on and off by the document authorizer.
- kul:notes - this is a tab that allows us to add notes to our document
- kul:adHocRecipients - this is a tab that allows us to add ad hoc workflow recipients to a document
- kul:tab - this tag draws a tab for us, so that we can create our own. We'll take a closer look when we examine how to build a tag file, but basically, the HTML we want to display within a given tab is wrapped within this tag. Annoying, we also need to wrap that content within a <div class="tab-container" align="center"> tag.
- kul:htmlControlAttribute - this very flexible tag is handed the name of a property on a document and the data dictionary entry for a document; it the displays the proper attribute for the given property, based on the attribute within the data dictionary. This tag has a lot of other interesting attributes as well.
- kul:htmlAttributeLabel and kul:htmlAttributeHeaderCell - these two tags are handed the name of a property on a document and the data dictionary for the document, and then they display the label of the attribute as defined in the data dictionary. kul:htmlAttributeHeaderCell wraps its output in <th> tags.
- sys-java:accountingLines - this wildly flexible tag shows the accounting lines for a document. More information on its uses can be found on the page about the Accounting document framework..
- gl:generalLedgerPendingEntries - this tag shows the general ledger pending entries for a financial processing document
Each module defines its own set of tags that we will likely need to know as we work on the module itself. It is often useful to know the Struts taglibs, especially the html: and logic: taglibs. Finally, knowledge of the JSTL standard taglibs is very helpful as well.
Creating a taglet, or, The Return of TD
Now that we've got a library of tags, let's see how hard it is to create our own taglet. Because taglets throw us back to the land of HTML, the files get much, much larger, so we're going to show some extracts here, specifically from fin:items. All KFS tags belong in the work/web-root/WEB-INF/tags/{module code} directory; the module code for Financial Processing tags is "fin", so the items.tag file can be found in work/web-root/WEB-INF/tags/items.tag.
As we look through the tag, we see that the kfsTldHeader.jsp is once again included. After that, any attributes for the tag are declared; in this case, we've got only one attribute, editingMode, which is the document authorizer editing mode. Note that any tag can access the Form class under the variable KualiForm, and therefore, it can access the document as KualiForm.document.
Next, we see the tab for the items is created:
<kul:tab tabTitle="Items" defaultOpen="false" tabErrorKey="${KFSConstants.ITEM_LINE_ERRORS}">
We give our tab a title ("Items"), we note whether it is open on default or not, and finally we create a tabErrorKey, which is the error paths of any errors to be displayed in this tab. In this case, any errors with the paths: "newItem*,document.item*" will be displayed in the tab. Note, by the way, that we can access any constants through KFSConstants. Cool.
Next, we retrieve any business object data dictionary entries we need to display business object or document fields on the page:
<c:set var="itemAttributes" value="${DataDictionary.InternalBillingItem.attributes}" />
Once again, we have a magical singleton, DataDictionary, which holds all of the data dictionary entries.
Then we do some display:
<div class="tab-container" align=center> <div class="h2-container"> <h2>Items</h2> </div> <table cellpadding="0" cellspacing="0" class="datatable" summary="Items section">
This tag correctly has the div for the tab content. Headers must also be wrapped in divs. Finally, we open a table; tables on KFS forms should use the CSS class of "datatable".
Next, we need to show the header row for the table:
<tr> <kul:htmlAttributeHeaderCell literalLabel=" "/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.itemServiceDate}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.itemStockNumber}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.itemStockDescription}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.itemQuantity}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.unitOfMeasureCode}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.itemUnitAmount}"/> <kul:htmlAttributeHeaderCell attributeEntry="${itemAttributes.total}"/> <c:if test="${not readOnly}"> <kul:htmlAttributeHeaderCell literalLabel="Actions"/> </c:if> </tr>
Notice the use of the kul:htmlAttributeHeaderCell tag.
Then, we show a row for the new item:
<c:if test="${not readOnly}"> <tr> <kul:htmlAttributeHeaderCell literalLabel="add:" scope="row"/> <td class="infoline"><kul:dateInput attributeEntry="${itemAttributes.itemServiceDate}" property="newItem.itemServiceDate" /></td> <td class="infoline"><kul:htmlControlAttribute attributeEntry="${itemAttributes.itemStockNumber}" property="newItem.itemStockNumber" /></td> <td class="infoline"><kul:htmlControlAttribute attributeEntry="${itemAttributes.itemStockDescription}" property="newItem.itemStockDescription" /></td> <td class="infoline"><kul:htmlControlAttribute attributeEntry="${itemAttributes.itemQuantity}" property="newItem.itemQuantity" /></td> <td class="infoline"><kul:htmlControlAttribute attributeEntry="${itemAttributes.unitOfMeasureCode}" property="newItem.unitOfMeasureCode" /></td> <td class="infoline"><kul:htmlControlAttribute attributeEntry="${itemAttributes.itemUnitAmount}" property="newItem.itemUnitAmount" styleClass="amount" /></td> <td class="infoline"><!-- no total until it's added --></td> <td class="infoline"> <div align="center"> <html:image property="methodToCall.insertItem" src="${ConfigProperties.kr.externalizable.images.url}tinybutton-add1.gif" alt="Insert an Item" title="Insert an Item" styleClass="tinybutton"/> </div> </td> </tr> </c:if>
In this, it's important to point out a couple things. First of all, controls are drawn using the kul:htmlControlAttribute tag...except for the date field, which uses the kul:dateInput tag. Notice, too, that the "add" button uses the Struts-defined html:image tag to make a clickable image button.
The rest of the tag defines how to display the old lines of items, which is pretty similar to how the new item line is displayed (though instead of using "newItem." as the property, they use "document.items[KULRNE:${ctr}]." (all property names in kul: tags are relative to KualiForm). Finally, a total is displayed and the tab's div and kul:tab tag are closed:
<tr> <td class="total-line" colspan="7"> </td> <td class="total-line" ><strong>Total: $${KualiForm.document.itemTotal}</strong></td> <c:if test="${not readOnly}"> <td class="total-line"> </td> </c:if> </tr> </table> </div> </kul:tab>
If we're certain how a property should be displayed, it's certainly permissible to use the JSP EL ${...} syntax to display it.
That's our tag, then. How does it get used? Well, on InternalBilling.jsp, we see that it's use is pretty easy:
<fin:items editingMode="${KualiForm.editingMode}" />
Kuali documentation is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.
Kuali software is licensed for use pursuant to the Affero General Public License, version 3.
Copyright © 2014 Kuali, Inc. All rights reserved.
Portions of Kuali are copyrighted by other parties as described in the Acknowledgments screen.
Kuali ® is a registered trademark of the Trustees of Indiana University.