Professional Documents
Culture Documents
2
Application Developer Guide
Notice
Limitations on Warranties and Liability
Saba Software, Inc. reserves the right to make changes in information contained
in this document without notice.
In no event shall Saba or its suppliers be liable for any damages whatsoever
(including, without limitation, damages for loss of business profits, business
interruption, loss of business information, or any other pecuniary loss) arising
out of or relating to this documentation or the information contained in it, even
if Saba has been advised of the possibility of such damages and whether arising
from tort (including negligence), breach of contract or otherwise.
This document may only be reproduced and distributed in whole for use by
licensed users. No part of this document may be reproduced in any form for any
other purpose without the prior written consent of Saba Software, Inc.
The software described in this documentation is copyrighted and is confidential
information and a proprietary product of Saba Software, Inc.
U.S. GOVERNMENT RESTRICTED RIGHTS. If licensee is the United States
Government or any contractor thereof, all licenses granted in the License
Agreement accompanying this product are subject to the following: (i) for
acquisition by or on behalf of civilian agencies, as necessary to obtain protection
as "commercial computer software" and related documentation in accordance
with the terms of this Commercial Software Agreement as specified in 48 C.F.R.
12.212 of the Federal Acquisition Regulations and its successors; (ii) for
acquisition by or on behalf of units of the Department of Defense ("DOD") as
necessary to obtain protection as "commercial computer software" and related
documentation in accordance with the terms of this commercial computer
software license as specified in 48 C.F.R. 227-7202-2 of the DOD F.A.R.
Supplement and its successors.
Saba, the Saba logo and Centra are registered trademarks of Saba Software, Inc.
and Saba and Saba Centra product names are the trademarks of Saba Software,
Inc. All other product names mentioned in this manual are the property and may
be trademarks or registered trademarks of their respective owners, and are used
for identification purposes only.
| Introduction | 4
Published: 11/22/2013
Part Number: G-ADG-SCR72000-1
| TOC | 5
Contents
Preface: About This Guide
..........................................................................................xi
Intended Audience
.....................................................................................................................................................xi
How to Contact Saba
..................................................................................................................................................xi
| TOC | 6
| TOC | 7
<wdk:pageWidget>
.................................................................................................................................................195
<wdk:parameters>
...................................................................................................................................................205
<wdk:presence>
......................................................................................................................................................206
<wdk:promptForSave>
............................................................................................................................................208
<wdk:sabaPicker>
...................................................................................................................................................210
<wdk:saveLink>
......................................................................................................................................................212
<wdk:script>
...........................................................................................................................................................213
<wdk:table>
............................................................................................................................................................217
<wdk:textarea>
........................................................................................................................................................228
<wdk:timeInput>
.....................................................................................................................................................230
<wdk:tree>
..............................................................................................................................................................232
<wdk:validatorWidget>
..........................................................................................................................................257
Component Dictionary Support
..............................................................................................................................258
Example
..................................................................................................................................................................259
| TOC | 8
Non-WDK Programming
............................................................................................................................285
| TOC | 9
| TOC | 10
Security Constraints
....................................................................................................................................422
Preface
About This Guide
The purpose of this document is to provide the information and guidelines that application developers need to customize
Saba Cloud using Saba's public Java API. The document demonstrates how to customize existing application pages as
well as how to build new pages, taking advantage of the application functionality encapsulated in Saba's Java API.
Intended Audience
This guide is intended for use by the application developers responsible for developing Web applications using Saba's
Web development platform. This document is intended for Saba consultants and for Saba customers. It is also used
internally as a ramping tool for new developers.
The information in this guide assumes you have the following minimum skill set and/or knowledge:
Chapter
1
Application Platform Overview
Topics:
Introduction
System Architecture
Security Issues
Internationalization Support
Introduction
Saba's application development platform is based on 3-tier distributed object architecture. Saba's 3-tier architecture
comprises the following elements:
System Architecture
Saba's system-level architecture consists of several functional modules layered on top of a set of shared business objects
and core services that provide common application functionality. The functional modules include:
Learning
Ordering
Performance
Content Management
Resource Managment
Each functional module includes objects and services specific to the particular application, as well as a set of common
business objects that provide core functionality.
The common business objects layer defines a set of business objects shared across all Saba applications and supports
linkages between the applications.
Supporting the applications and common business objects layers is the core services layer, which provides common
application functionality, including services such as payment processing, business rule configuration, and reporting.
The foundation of Saba's system architecture is the platform layer, which provides the underlying infrastructure for
developing and managing complex enterprise applications. This includes:
Definition of a core set of abstractions for security, persistence components, and so on, through Saba's Business
Object Server.
Ability to generate web-based application pages with dynamic content, through Saba's Web Developer Kit.
The layered architecture avoids circular dependencies by requiring that all dependencies be directed downwards. A
vertical application can have dependencies on one or more sets of common business objects but not on other applications.
Similarly, common business objects can depend on core services and on other common business objects, but not on
applications.
This following diagram illustrates Saba's system architecture:
Security Issues
Access to most functionality in a Saba application requires user authentication. A successful login results in a set of
privileges for the individual user in the session environment, along with a session key handle. The privileges take into
account the security roles assigned to the user, as well as any other restrictions associated with the particular environment
or access. Because security in Saba applications is implemented at the business object level, the system checks security
privileges whenever saving or restoring anything to or from the database.
Internationalization Support
Saba enables you to create fully internationalized applications that you can easily localize. Saba's provides support at
the back?end database level for storing locale-specific values.
Localizable Objects
Saba provides the ability to define locale-specific values for some attributes (usually the name and description attributes)
of many objects in the system. When a user defines a value for a localizable object, the system stores the value in a table
specific to the user's locale. Other users who share the locale see this value. However, users with a different locale do
not see this value. Instead, they see only the value entered by users who share their locale.
The only time a user (user "A") belonging to a locale (locale "A") can see the value associated with locale "B" is when
user "A" retrieves a record that has been created and saved by a user belonging to locale "B".
For a complete list of localizable objects, see Appendix C, "Localizable Objects"Appendix C, "Localizable Objects" on
page C-1.
Chapter
2
Customizing Saba
Topics:
Introduction to Saba
Customizations
UI Configuration: Overview
Script Customizations: Overview
Coding Customizations: Overview
Saba provides several ways to customize Saba Cloud, ranging from simple,
application-wide theme changes to intricate customizations written in Java.
| Customizing Saba | 18
Configuration: Settings that affect how the Saba applications run. Most changes here affect all Saba sites running
from a single installation. This includes such operations as changing attribute constraints, custom fields, and business
rules. (These can all be changed with the Saba Administrator UI.) This material is covered in the Catalog Administrator
Guide. It is not further discussed in this book.
Configurable UI: This covers look-and-feel options that apply across an entire Saba desktop (a view into a Saba
site). Changes to the UI do not let you modify behavior of specific application pages.
Scripting: This covers modifications to Saba application pages that do not require coding in Java. This can include
modifying pages to present the same data in different ways, as well as other web-related changes (such as adding
links to other application pages).
Coding: This covers any customizations that require Java code. This is the most powerful and versatile kind of
customization. Custom Java code can be integrated with Saba application pages, or launched separately.
These options are listed in order of increasing complexity and power. If you have a customization to make, look down
that list and use the first option that works. For example, to change the color scheme for an entire Saba installation, make
a theme customization, perhaps by modifying one of the style sheets.
UI Configuration: Overview
Saba's System Administration screens provide a wide range of UI configuration options. You can easily customize the
system's color schemes, menu configurations, and graphics. You can also set up several different user interface themes
for the same Saba site and make different themes available for different users. For example, you can use one color
scheme for system administrators and another scheme for ordinary users.
For detailed information about the Saba system administration screens, refer to the System Administrator Guide.
| Customizing Saba | 19
Chapter
3
WDK Page Tutorial
Topics:
Overview
Writing a Simple Portlet
Using Widgets
Command Objects and Visitors
This chapter describes how to write a WDK application page. The chapter covers
a wide range of possible page types, from the very simple to the elaborate.
Overview
Saba application pages are written in our WDK format. This format is described in detail in Chapter 3, "Scripting
Customization (WDK)"Chapter 3, "Scripting Customization (WDK)". A WDK application page is made of three files:
The control file is the master file. It specifies the other two files used by the application page, assigns a unique
identifier number to the page, and specifies certain other high-level resources (such as the widget library used by
the page).
The model file specifies the data displayed in the application page, and also specifies which widgets are available to
the page.
The view file specifies how the data and widgets are arranged on the application page.
These files are all written in XML. The control file uniquely identifies the application page. That is, by specifying a
control file, you have specified an application page. By contrast, a single model file can be used by several different
application pages. For example, a single model file can present information about users, and several different view files
can present the model file's data in different ways. For example, one view file can display a table with all the data,
another can display only certain columns of the table, and another can display a stripped-down version suitable for
displaying in a portlet.
It is possible to use a single view file with several different model files, but this is far less common than using several
views with a single model. Usually, a view file is tailored to a specific model file.
The model file is compiled to create a Java class. This compilation is done once for each model file. Once the Java
class is created, it can be used every time a user opens an application page which uses that file. The compiled Java
class generates XML output, which contains the data displayed on an application page.
The view file is used to transform the XML output into the web page that is displayed by a user's brower. It produces
a web page with XHTML and Javascript. There is not usually any Java in the output, so the user's browser does not
need to be Java-enabled. (Of course, you could write an application page which runs a Java applet if you wished.)
The control file identifies the model file and view file used by the application page. When you want to add an
application page into the system, you do it by creating a link (or a menu item) which opens that control file; Saba
then uses the control file to open the view and model file.
Thus, when a user launches an application page, the system follows this sequence of events:
1. The system checks the control file to see what model file and view file are used by the application page.
2. The system checks to see if the model file has already been compiled into a Java class. If it has not, the system
compiles that model file.
3. The system creates a Java object from that class, and uses the object to generate the XML data used by the application
page.
4. The system uses the view file (and any associated stylesheets, like the widget definition stylesheets) to transform
the XML data into an XHTML/Javascript file.
5. The system sends that file to the user's browser, which displays the finished application page.
If the application page is a portal, the system also follows this sequence for each portlet on the page.
The way Saba constructs the finished application page has a few implications you should keep in mind:
The first time a person runs an application page, Saba has to compile the model file into a Java class. This greatly
slows down the application's response time for that page. The next time anyone views that page (even with different
data), Saba can use the cached Java class, greatly improving response speed. (You can also use a Saba utility to
compile all the application pages at once, instead of waiting for users to load the pages.)
When a user views an application page, Saba checks to make sure that the compiled model class is up-to-date. If the
model file is more recent than the Java class, Saba recompiles the class. However, some application servers keep
caches of Java classes used. As a result, if you change a model file while Saba is running, and the model file has
already been used since the last time the application server was launched, the server may use its cached version of
the class instead of the recompiled version based on the new model file. This keeps the changes from showing up
in the compiled application page. For that reason, after you make changes to model files, you may need to restart
your application server to view the changes. (This is not necessary if you make changes to the view or control file.)
You might create a brand-new page entirely from scratch. In that situation, you would need to create a control, model,
and view file, and insert the page into the application.
You might edit an existing WDK page. In that case, you might duplicate an existing page, modify the duplicate, and
insert the new page into the application. You could also edit the existing page in place.
If you are editing an existing application page, you can ignore much or all of what you see in the files and make a small
alterations. For example, you might decide that a particular table should show less information than it does. You could
do this by editing the view file to remove a particular column, and not altering the model or control files at all. On the
other hand, if you are creating a new page, you must understand how all three files are constructed, but you might not
need to know subtler details about how the components work. The page that you construct can be simpler than typical
Saba application pages.
Of course, there isn't always a clear line between creating new pages and editing existing ones. Usually when you create
a brand new application page, do it in stages, gradually building up the functionality from one draft of the page to the
next (and borrowing code from existing application pages that do similar things). That is how this tutorial is structured.
It begins by describing how to create a simple portlet, then describes how to add functionality to the portlet, turn it into
an application page, and then add advanced features.
Tutorial Description
This tutorial describes how to create a wide number of WDK pages. It shows how to build simple application pages
from scratch, and also how to modify simple pages to create more complex ones.
Note: You may never need to create an entire application page. Usually, you are able to design web pages by
modifying existing WDK application pages.
The tutorial covers the following steps:
but portlets should be small, simple, and not make assumptions about how much screen space they can have. This ensures
that Saba can resize the portlet as necessary, and still have it be readable when displayed.
Because a portlet is small and self-contained, and because it is easy to install into Saba, portlets are a very useful way
to design and test WDK functionality. For example, very often the meat of an application page is a table displaying
information. You can build and test that table in a portlet. Then, once that code is working, you can expand it into a full
application page.
This section covers the following steps:
1. Writing a portlet that displays simple text, installing it in Saba, and viewing it.
2. Expanding the portlet to use some common widgets, and to follow Saba's best practices.
<?cocoon-process type="portlet"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="511111">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version> <wdk:model
rdf:resource="helloWorldPortlet.xml"/> <wdk:view
rdf:resource="helloWorldPortlet.xsl"/> <wdk:widgets
rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
</rdf:Description>
</rdf:RDF>
The bold-face sections are areas you usually would need to change when you create a new control file. In addition, if
the model file contains links to model files (including itself), include a <wdk:links> node, as described in
"<wdk:links>""<wdk:links>" on page 3-19. Other than that, your various conrol files can all look pretty much the same.
The parts of the control file that might change include:
Cocoon process type: The <?cocoon-process> tag must have a type attribute. This attribute is set to portlet
for portlet pages, and wdk for ordinary application pages.
ID Number: Every application page (including portlet pages) must have a unique ID number. Saba reserves all ID
numbers up to 500,000. You can use any integer above that for your application pages. The ID number is specified
in the <rdf:description> node's id attribute.
Model and view pages: The control file specifies the model file and the view file used by this page or portlet. This
is specified either with the full URL of the model and view files, or (more often) with the path relative to the location
of the control file. Thus, if the model, view, and control files are all in the same directory (as is usually the case),
you can enter the names of the files.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdk:labels>
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
</wdk:widgets>
</wdk:page>
</xsp:page>
As you can see, this model file contains very little information. The only information specific to the portlet is the text
associated with the hello_text label, "Hello world!"
<?wdklint ignoreRules="XSL_HTML"?>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss10="http://purl.org/rss/1.0/">
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
</xsl:template>
</xsl:stylesheet>
The defaultview.xsl file is responsible for creating the general framework of the portlet, including an <HTML>
node. To fill in the contents of the <HTML> node, that stylesheet invokes the wdk:model template which you write.
The result is a portlet page containing a single <p> node, which contains the value of the hello_text variable:
<HTML>
cellspacing="1" cellpadding="0">
<tr><td>Hello, world!</td></tr>
</table>
</HTML>
Note: As noted in Appendix A, "Designing Portlets"Appendix A, "Designing Portlets", a portlet should consist of
either an HTML table (and its contents), or a Saba table widget. Thus, the "Hello, world!" text is put inside an
HTML table with one row and one column.
When you write the portlet page manager, you must know where the application page's control file is installed. (The
Saba application can then examine the control file to find the locations of the model and view files.) In this case, we
assume that the control file is installed as /tutorial/portlet/helloWorldPortlet.rdf.
package com.saba.client.portal;
import com.saba.exception.SabaException;
import com.saba.portal.AbstractPortletPageManager;
import com.saba.portal.PortletPageManager;
/**
* This class registers the page for the Hello World portlet.
*/
public HelloWorldPortletPageManager() {
/**
* registerPage.
*/
registerPage(PortletPageManager.kDefaultDisplay,
"/tutorial/portlet/helloWorldPortlet.rdf");
} /* init */
} /* HelloWorldPortletPageManager */
After you compile this class, install it into Saba. Place the class file in the Saba application server's Java class path, then
add the portlet and its manager using the Saba system administration module (as described in the System Administrator
Guide).
Create appropriate resource entries in that resource bundle. In this case, we assume that the resource bundle is named
"my_custom_pages", and we add a single entry to the default resource file:
kI18n511111helloWorldLabel=Hello, world!
Note the naming convention followed. All of an application page's resources should be given names that fit the pattern
kI18n<page_ID_number><label_name>
The model file can then load any resources it needs from that bundle. To use the value of that label, you would change
the model file described above to the following. (Once again, changes are in bold-face.)
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdktags:i18n.load resource="my_custom_pages"/>
by the view file -->
<wdk:labels>
<wdk:label name="hello_text"><wdktags:i18n.label
name="kI18n511111helloWorldLabel"/></wdk:label>
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
</wdk:widgets>
</wdk:page>
</xsp:page>
When Saba compiles the Java code for the model page, it transforms the <wdktags:i18n.label> node into an
appropriate command to load the text from the resource bundle. Thus, the page uses whatever label is best suited for
the current locale.
The view and control files do not need to be changed to make use of the new label. The view file loads the contents of
the hello_text XML label, as before.
Using Widgets
The previous example showed how to create an application page or portlet which uses straight xHTML. You could use
exactly the same technique to display any other code which is supported by your target browser, such as Javascript or
Java applets.
Saba also provides a powerful library of widgets which you can use in your pages and portlets. There are several
advantages to using widgets:
The widgets provide a wide range of functionality, including special tools for accessing the Saba data store.
The Saba application automatically adjusts the widgets to match whatever theme and skin is in use.
Saba provides a syntax for dynamically creating as many widgets as necessary for the data you want to display. For
example, you might create a page which displays all students in a class, along with a "delete student" button for each
student. In that case, you could define the "delete student" widget once, then use WDK directives to instruct Saba
to create a copy of that widget for each row in the "students enrolled" table.
This section of the tutorial describes how to add a widget to the "Hello World" portlet defined in the previous section.
In this case, the portlet has a single widget, a textArea widget which can be used to display or input text. The following
section describes how to create multiple widgets dynamically to match whatever text your application page may be
displaying.
If you use a widget, you must insert code in two places:
The model file defines the widget itself, specifying what parameters are passed to the widget and how it should be
displayed.
The view file specifies where in the file the widget should be displayed.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdktags:i18n.load resource="my_custom_pages"/>
<wdk:labels>
<!-- Label is no longer needed! -->
</wdk:head>
<wdk:form method="POST">
</wdk:labels>
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:input name="helloText">
name="kI18n511111helloWorldLabel"/></value>
</wdk:input> </wdk:widgets>
<value><wdktags:i18n.label
<viewOnly>true</viewOnly>
</wdk:page>
</xsp:page>
As you can see, this version of the portlet no longer has a label. The view file doesn't display the text directly, by accessing
the label. Instead, it displays the helloTextwidget, which contains the message.
<?wdklint ignoreRules="XSL_HTML"?>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss10="http://purl.org/rss/1.0/">
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
</table>
</xsl:template>
</xsl:stylesheet>
The bold-faced statement applies the standard Saba widget library's stylesheet to the selected widget. In this case, the
widget is identified by its name, but you could identify the widget by any other XSLT path. For example, you could
search for a widget with a particular ID, or for a widget that's the child of some other node.
This example uses a simple command object to generate the XML node. In this example, the command object generates
a static, fixed node, containing data which is displayed in the application page. Of course, the true strength of command
objects is that they can generate their XML output dynamically, and can retrieve information from the Saba data store.
In addition, this application page is designed as a stand-alone page, instead of as a portlet. There is very little difference
between writing a portlet and writing an application page. However, you must add the application page to the Saba
menus, instead of providing a portlet that users can add to their portal pages. For more information about adding new
application pages into the menu system, refer to the System Administrator Guide.
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="522222">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version> <wdk:model
rdf:resource="tutorialCommandPage.xml"/> <wdk:view
rdf:resource="tutorialCommandPage.xsl"/> <wdk:widgets
rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
</rdf:Description>
</rdf:RDF>
In this case, we assume that the control file is in the same directory as the model and view files, and that all three files
have the root name tutorialCommandPage (with the various appropriate file extensions). We give this application
page the ID number 522222, and designate it as a standalone application page, instead of a portlet.
uses the simplest version of that directive, in which it specifies that a particular command object should be created, and
that command object's execute method should be invoked.
A command object must implement Saba's ICommand interface.Usually, it does this by extending the
AbstractCommand class. If you create a new class based on AbstractCommand, you must provide two methods:
You must write a no-argument constructor for the object. If the command object expects to be passed parameters,
the constructor method performs some initial processing. If the command object doesn't pass or return parameters,
you can write an empty constructor (as in this example).
You must write the doExecute method. This method is what generates the XML output. The method does this by
using a visitor. The visitor is a special-purpose object created by AbstractCommand's code, which produces XML
representations of Java objects. If the visitor examines a simple Java type (like a number or a string), it generates an
appropriate XML node. If it visits Saba object which implements IXMLObject, it generates a node for the object,
then recursively visits each field in the object. Thus, if you want to generate an XML representation of some Saba
data, all you usually need to do is create the appropriate Saba object, then pass it to the XML visitor.
In the case of this example, the command object builds its XML output a node at a time, creating a static data set.
/*
* TutorialCommand.java
*/
package com.mycorp.tutorial
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import com.saba.web.dk.AbstractCommand;
import com.saba.xml.IXMLVisitor;
import com.saba.xml.SabaXMLType;
/**
*/
*/
public TutorialCommand()
/**
* by node.
<people>
<person>
<last_name>Smith</last_name>
<first_name>John</first_name>
</person>
</people>
*/
IXMLVisitor visitor )
throws Exception
visitor.endVisit(null, "person");
visitor.endVisit(null, "person");
visitor.endVisit(null, "person");
visitor.endVisit(null, "people");
}
This object must be compiled, and the class file placed in your application server's class path.
Instead of containing the data directly in the file, the model file invokes a command object to generate XML data.
(It uses the command object defined in the last section, TutorialCommand.)
The model file uses a table widget to display the XML data. The model file attaches the table to the data generated
by the command object, by using a <wdktags:attachTo> directive. The table widget automatically displays as
many rows as are needed by the data.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdktags:i18n.load resource="my_custom_pages"/>
</wdk:head>
<wdk:form method="POST">
<!-- Model information is in this tag. This is where we make the call to
<wdk:model>
<!-- In the output, the following tag is replaced
by
the XML node output from the command object.
-->
<wdktags:execute
commandObj="com.mycorp.tutorial.TutorialCommand"
/>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:table name="peopleTable">
<!-- The attachTo node
indicates that this widget is
linked to the 'people' node created by the
command object. -->
<wdktags:attachTo path="people"/>
<head> <!-Defines the heading row -->
<!-- Usually, you would use internationalizable labels for the heading row's labels.
In this example, we use
static text for
simplicity. -->
<column>First Name</column>
<column>Last Name</column>
</head>
<row path="person"> <!-- i.e. one
row for each
'person' node -->
<column>
<wdktags:nodeRef path="first_name"/>
</column>
<column>
<wdktags:nodeRef path="last_name"/>
</column>
</row>
</wdk:table>
</wdk:widgets>
</wdk:page>
</xsp:page>
The <wdktags:execute> directive invokes the command object; as we saw, that object generates the static XML
output
<people>
<person>
<last_name>Malfoy</last_name>
<first_name>Draco</first_name>
</person>
<person>
<last_name>Dumbledore</last_name>
<first_name>Albus</first_name>
</person>
<person>
<last_name>Weasley</last_name>
<first_name>George</first_name>
</person>
<people>
The model file also specifies that a table widget should be created, and attached to the <people> node. The table
widget specifies that it should have one row for each <person> node under the <people> node. Each row, in turn,
has one column for the first name and one for the last name. The widget thus generates a table like this:
First Name
Last Name
Draco
Malfoy
First Name
Last Name
Albus
Dumbledore
George
Weasley
Note that the table displays first name before last name, which is different from the order in the command object's output.
When you define the table widget, you are perfectly free to specify what order you want the data to appear in. You can
also choose to omit some of the data returned by your command object. For example, you could easily create a table
which displayed last names.
View File
This view file is very straightforward. All the model file's data is presented in a table widget. The view file needs to
display that widget.
<?wdklint ignoreRules="XSL_HTML"?>
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
<xsl:apply-templates
select="$wdkWidget[@name='peopleTable']"/> </xsl:template>
</xsl:stylesheet>
Chapter
4
WDK Widgets
Topics:
Widget Overview
Using Widgets
Widget Format
Widgets and Model Objects
The Standard Widget Library
<wdk:componentWidget>
<wdk:customFields>
<wdk:dateInput>
<wdk:deleteLink>
<wdk:domainPicker>
<wdk:file>
<wdk:finderQuery>
<wdk:finderResult>
<wdk:finderControl>
<wdk:form>
<wdk:frameworkLink>
<wdk:functionality>
<wdk:genericText>
<wdk:hiddenField>
<wdk:impresence>
<wdk:input>
<wdk:impresence>
<wdk:link>
<wdk:list>
<wdk:lovPicker>
<wdk:multistep>
<wdk:multisection>
<wdk:pageText>
<wdk:pageTitle>
<wdk:pageWidget>
<wdk:parameters>
<wdk:presence>
<wdk:promptForSave>
<wdk:sabaPicker>
<wdk:saveLink>
This chapter describes how to use UI widgets in Saba WDK application pages.
The chapter also includes a full reference of available widgets.
| WDK Widgets | 50
<wdk:script>
<wdk:table>
<wdk:textarea>
<wdk:timeInput>
<wdk:tree>
<wdk:validatorWidget>
Component Dictionary Support
Example
| WDK Widgets | 51
Widget Overview
WDK application pages do not code UI elements (such as menus and buttons) directly. Instead, application pages use
widgets. Widgets are defined in terms of their functionality. For example, instead of using a "text field", a WDK page
uses an input widget. The WDK engine then translates the widget into an appropriate representation for the desired
output format. If the application page is being rendered in HTML, the WDK engine generates appropriate HTML
components. But if, in some future release, other formats are supported (such as WML), the WDK engine could render
the widgets in an appropriate way for that format.
Widgets are used in this way in the WDK:
The model file defines which widgets are available. It specifies all the contents and attributes of the widgets (such
as the widgets' names and labels). The contents are often set dynamically. For example, a model page might use the
results of an <wdktags:execute> tag to set the contents of a list widget.
If a WDK page has a model object, Saba queries it for each widget to find out whether the widget is visible. It also
checks to see if the widget is editable and if it should be initialized from the HTTP request that launched the page,
if that is not explicitly specified in the model file.
The view file specifies how widgets are arranged on the page. It does not need to include all the widgets defined on
the model page. The model might provide a range of widgets, letting the view page choose those that are appropriate.
The control file, besides associating a particular view with a particular model, also specifies an appropriate widget
library file. This file is used by the WDK engine to transform the model page's widget tags into appropriate code
for the target language.
Currently, the only widget library provided is the master widgets file wdk_widgets.xsl. (This master file, in turn,
loads other library files providing various widgets.) This library transforms widgets into HTML UI components. The
widget library is specified in the control file with a <wdk:widgets> tag, which is a child of the <rdf:Description>
tag:
<wdk:widgets rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
You add widgets to an application page by adding XML code to the model and view pages. Each widgtet's XML tag
format is defined by an XSLT file. The various widget XSLT files are all included in the library's master WDK widgets
file (wdk_widgets.xsl). This is the file you specify in the application page's control file.
Using Widgets
As noted, if you are using a widget in an application page, you must edit two different files:
In the model file, you place a full description of the widget. This is an xml node that describes the widget's appearance,
behavior, and so on. The widget may be given a name, and may be attached to a particular XML node.
In the view file, you put an XML tag specifying where the widget goes. The widget might be identified by its location
(for example, "every link that is attached to a <person> node").
Some widgets can contain other widgets. For example, you might use the table widget to create a table, each of whose
rows has a button (created with the link widget). In that case, you include the container widget in the view file. Saba
builds that widget with all of its contents.
| WDK Widgets | 52
| WDK Widgets | 53
You may also want to associate widgets with data elements in the model object. For example, a particular page might
display a list of people. You might want to create a widget (such as an edit button) for each person in the list. In a
situation like this, the data model produced within <wdk:model> might contain a list of person nodes, as follows:
<wdk:model>
<people>
<person>
<name>Sam Malone</name>
<age>40</age>
<id>persn00001</id>
</person>
<person>
<name>Frasier Crane</name>
<age>38</age>
<id>persn00002</id>
</person>
</people>
</wdk:model>
This model represents the data we have about people. It does not specify how we interact with this data. However, we
can associate an edit button with each person, so when the button is clicked the edit page is shown for the given person.
| WDK Widgets | 54
To do this, define a single widget and specify that the widget be copied and attached to each individual <person>
node:
<wdk:widgets>
<wdk:link name="editButton">
node -->
<href>editPage.rdf</href>
<label>Edit</label>
<field>
<name>personId</name>
node --></value>
</field>
</wdk:link>
</wdk:widgets>
The <wdktags:attachTo><wdktags:attachTo> tag specifies that a copy of the button should be attached to
each <person> node that is a child of the <wdk:model><people> node. The generated XML page contains the
following content:
<wdk:form>
| WDK Widgets | 55
<wdk:model>
<people>
<person>
<name>Sam Malone</name>
<age>40</age>
<id>persn00001</id>
<wdk:link name="editButton">
<href>editPage.rdf</href>
<label>Edit</label>
<field>
<name>personId</name>
<value>persn00001</value>
</field>
</wdk:link>
</person>
<person>
| WDK Widgets | 56
<name>Frasier Crane</name>
<age>38</age>
<id>persn00002</id>
<wdk:link name="editButton">
<href>editPage.rdf</href>
<label>Edit</label>
<field>
<name>personId</name>
<value>persn00002</value>
</field>
</wdk:link>
</person>
</people>
</wdk:model>
</wdk:form>
<wdk:widgets>
| WDK Widgets | 57
</wdk:widgets>
Note the following:
The <wdktags:attachTo> directive creates a copy of the widget under each model node selected by the path
attribute. Notice that in this case two <wdk:link> elements were created, one inside each person element. This is
because the <wdktags:attachTo> directive attached the widget to every <person> node.
The <wdktags:nodeRef> directive retrieves the value of sub-nodes of the node the widget is attached to. In the
above example this directive was used to generate a dynamic prompt (to include the user's name in the prompt) and
a dynamic field value (to use the id of the person). This latter use is similar to how runtime variables were used in
the .saba pages used by earlier versions of Saba.
All of the generated links have the same name attribute, "editButton". The name is copied from the original
version of the widget (in the <wdk:widgets> section). This is not a problem, since each widget can be identified
by an XML path that combines its location and name.
The <wdk:widgets> section became empty because the widget (the <wdk:link> ) has been copied to the model
nodes. However, if a widget does not use the <wdktags:attachTo> directive, then that widget
remains inside the <wdk:widgets> element and remain accessible by the widget and view transformations.
<xsl:apply-templates select="wdk:widget[@name='widget_name']"/>
This node is usually a child of an appropriate <xsl:foreach> or something similar, which identifies the location of
the widget in the generated model file.
For example, suppose the model file were the one described above, which attached an edit link to each <person>
node. You could generate a list of people, with each user's name and an edit button, by putting the following code in
the view file:
<xsl:for-each select="people/person">
<xsl:apply-templates select="wdk:widget[@name='editButton']"/>
<BR />
</xsl:for-each>
The <xsl:for-each> directive iterates through each <person> node under the <people> node. The
<xsl:apply-templates> is applied, in turn, to each <person>. It displays the widget under that node whose
name is 'editButton'.
| WDK Widgets | 58
If a widget is not attached to a particular model node, you can display it with the following element:
<xsl:apply-templates select="$wdkWidget[@name='widget_name']"/>
Widget Format
This section describes the format of a widget declaration in the model page. It also describes some of the common
attributes and child nodes that are available for most or all widgets. The specific attributes and children available to each
widget are described in "The Standard Widget Library".
You declare a widget by adding a child to the model file's <wdk:widgets> node. The widget looks something like
this:
</wdk:widget_name>
The specific contents of the widget vary from one widget to the next. However, there are several attributes and child
nodes that you usually see repeated from one widget to the next.
'name' attribute
This string is used to identify the widget. Every widget must have a name. However, the name does not have to be unique
on the model page. You can have several widgets with the same name, even if they are children of the same node.
As described above, when a widget has an <wdktags:attachTo> child, it is duplicated as necessary and made a
child of every qualifying node. This can result in many widgets with the same name, but in different locations.
<id>
Many widgets can have an <id> node. This node contains a text string that is used as the widget's identifier in several
circumstances. For example, if a widget generates an HTML form element, the <id> is often used as the name of the
element, or as the root of that name. For details on how each widget uses its <id> node, see the widget's reference
section.
Note: <id> is usually an optional element. If you do not specify an <id> for a widget, Saba uses the widget's
name as an ID. However, if a widget is being duplicated and attached to many data nodes, you may want to explicitly
specify an ID for each widget, by basing it on the data the widget is attached to. An example of this is shown in
"Defining in the Model File".
<data>
Many widget elements have <data> subelements, which are used to generate data to attach to a particular part of the
widget. For example, tree nodes have <data> subelements that specify what data to display in that node. In most cases,
| WDK Widgets | 59
the <data> subelements follow a common format. (If a widget uses <data> nodes differently, that is noted in the
widget's documentation.)
A <data> element may contain any of the following
<wdktags:attachTo>
This tag attaches the parent widget to the model node or nodes designated by the path and root attributes. The model
node are identified by following the path from the <wdk:model> node or from the node specified by the root attribute.
If more than one node matches the path, the widget is duplicated, and a copy is attached to each matching node. Once
the widget has been attached to a model node, elements in the widget can use data from that node with the
<wdktags:nodeRef> tag (described in "<wdktags:nodeRef>""<wdktags:nodeRef>" on page 3-34).
The <wdktags:attachTo> tag has the following attributes:
Table 3-20:) Properties of<wdktags:attachTo> Tag
Name
Required
Value
path
No
An Xpath expression identifying a node in the model section. The widget is attached
to that node. (This is a relative path, with the root attribute as the path's root.)
Either path or pathExpr must be specified.
pathExpr
No
A Java expression that evaluates to an XPath expression specifying the model node
to attach to.
Either path or pathExpr must be specified.
root
No
The root node of the path. The default is the node corresponding to the
<wdk:model> node in the model page.
| WDK Widgets | 60
The following example defines an input field called Name and attaches it to the <leaf> element in the <model>
node:
<wdk:input name="Name">
<label><wdktags:i18n.label name="kI18n9001Name"/></label>
<id>name</id>
<value><wdktags:nodeRef path="name"/></value>
</wdk:input>
When the model file is processed, the <wdktags:nodeRef> tag is replaced with the contents of the
model/leaf/name node.
<initializeFromRequest>
If this node is present, the widget should try to initialize itself from the request object that generated the application
page. This is only useful in certain limited circumstances.
Certain operations on an application page can force the entire page to be reloaded. For example, if you change the domain
choice with a domain picker, that can force Saba to regenerate and redisplay the page. When this happens, you usually
want all the other form elements on the page to keep whatever values the user may have entered.
The ordinary way to arrange this is to pass parameters to the successor page, containing all the data the page needs to
repaint itself and properly initialize all widgets. However, in some circumstances, this may not be feasible.
The <initializeFromRequest> node takes advantage of the fact that when the HTTP request is made, all the
contents of the form elements are passed along as part of the request. If a page is reloading itself, Saba can properly set
up the form elements by extracting the information from the request object.
To indicate that a widget should be initialized from the request object, add an <initializeFromRequest> node.
The node has no attributes or children. Its contents must be either:
Table 3-20:) Contents of <initializeFromRequest>Tag
Value
Meaning
true
The form element should be initialized from the request object. This overrides any other nodes that
may specify a value for the form element.
false
The form element should not be initialized from the request object. (This is the default behavior.)
If a widget is marked "initialize from request", and the page is not being reloaded, the behavior is undefined. In that
situation, the request object is generated by a completely different page. Saba tries to find a value for the form element
| WDK Widgets | 61
in that request object. Most likely it fails, and the element is treated as if <initializeFromRequest> were set to
<false>. However, Saba may find inappropriate data in the request object and initialize the element improperly.
For this reason, the contents of this tag should not usually be a simple, hardcoded true or false string. Instead, if
you use this tag at all, you should use an XML tag or Java expression that evaluates to true or false as appropriate.
If the application page has a model object, Saba queries that object for each widget that does not contain an explicit
<initializeFromRequest> node, and finds out if the widget should be initialized from the request object. This
is described further in "Widgets and Model Objects".
<viewOnly>
If this node is present and contains the value true, the widget appears in a view-only form. The way this is done varies
depending on the widget. Acceptable values for the node include:
Table 3-20:)Contents of<viewOnly>Tag
Value
Meaning
true
false
The widget should be displayed in an editable form, if appropriate for the widget type. (This is the
default behavior.)
If the application page has a model object, Saba queries that object for each widget that does not contain an explicit
<viewOnly> node, and find out if the widget should be displayed as view-only. This is described further in "Widgets
and Model Objects".
| WDK Widgets | 62
| WDK Widgets | 63
<wdk:breadCrumb>
Description
Lets the user view and manage an object's attachments (files and hyperlinks).
<wdk:chart>
<wdk:chartToolbar>
<wdk:componentPick~
er>
<wdk:componentWid~
get>
<wdk:customFields>
<wdk:dateInput>
<wdk:deleteLink>
<wdk:domainPicker>
<wdk:file>
<wdk:finderQuery>
<wdk:finderResult>
| WDK Widgets | 64
Widget Name
Description
<wdk:finderControl>
<wdk:form>
A form element.
<wdk:frameworkLink>
<wdk:functionality>
Designates that widgets belong to a Saba functional area, and should be turned on
or off depending on whether that functionality is enabled.
<wdk:genericText>
Text entry field. Depending on the maximum size, Saba displays it as a single-line
input box or a multi-line text area.
<wdk:hiddenField>
A hidden element.
<wdk:input>
<wdk:link>
<wdk:list>
<wdk:lovPicker>
Links and buttons. One click can execute a sequence of actions, can invoke .saba
and WDK pages, and can invoke popup windows.
A combination of a drop-down menu and a popup window. Users can launch the
popup window to select an appropriate value, which is selected in the menu.
<wdk:pageText>
<wdk:pageTitle>
Specifies an application page's title and other properties of the application page.
<wdk:pageWidget>
<wdk:parameters>
| WDK Widgets | 65
Widget Name
Description
<wdk:presence>
Works with the SabaDialog code to graphically display whether a particular user is
available in real-time.
<wdk:promptForSave>
Monitors to see if a user tries to leave the page without saving his or her work. If
the user does, the widget pops up a dialog box asking if he or she means to do that.
<wdk:sabaPicker>
A combination of a text entry field and a popup window. Users can launch the popup
window to select an appropriate value, which is placed in the text field.
<wdk:sabaPick~
er><wdk:sabaPicker>
<wdk:saveLink>
<wdk:script>
<wdk:table>
<wdk:table>
<wdk:textarea>
<wdk:timeInput>
<wdk:tree>
<wdk:validatorWid~
get>
A widget to make sure the user enters proper information in all "required" widgets.
The following sections describe the widgets provided by the standard library, and describes the way they behave when
transformed into HTML/Javascript by the WDK engine.
| WDK Widgets | 66
<wdk:attachments>
Used to add an Attachments widget to a page. This lets the page add, view, or modify file or URL attachments to a
Saba object that supports them.
The <wdk:attachments> tag has one attribute:
Table 3-20:)Attributes of<wdk:attachments> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
Yes
attachmentOwnerId
onAction
| WDK Widgets | 67
Child Element
Required
Value
node has a single child, <mainpage>, which contains the
fully qualified .rdf file of the application page to load.
No
No
Used to override the default title for the table. The default title
for the table is 'Attachments'.
No
No
No
No
No
viewOnly
title
showCategory
showType
showLocale
showPrivate
displayPrivateAttachments
No
Used for the main page to specify its own javascript function
for the attachment popup to call when it closes. This allows
the main page to pass in any parameters needed for refreshing
itself.
No
No
If false, the print link is not shown and table contents cannot
be printed. Defaults to true.
callbackFn
export
| WDK Widgets | 68
Example
<wdk:attachments name="attachmentsTable">
<attachmentOwnerId><xsp:expr>id</xsp:expr></attachmentOwnerId>
<onAction>
<mainPage>/common/party/organization/editOrganizationInternal.rdf<
/mainPage>
</onAction>
<viewOnly>true</viewOnly>
<callbackFn>javascriptFunctionName</callbackFn>
</wdk:attachments>
<wdk:breadCrumb>
Used to add a "breadcrumb" to a page (showing the route of application pages the user followed to reach this page, and
linking to the earlier pages).
Note that this widget must be placed before the <wdk:pageTitle> tag, in order for the bread crumbs to be properly
displayed as part of the page title.
The <wdk:breadcrumb> tag has one attribute:
| WDK Widgets | 69
name
Required
Yes
Value
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
The name of the generated form element. If this is not specified, the value
of the widget's name attribute is used.
No
No
The last label to display in the breadcrumb trail. The default value is the
name of the application page.
No
Used to specify the previous link or links in the breadcrumb trail. You
may have as many <link> nodes as you like. They are specified in order,
from first ("top") page in the breadcrumb trail to last. This element has
required subelements, which are described in "<link> Child Elements".
id
maxChar
lastLabel
link
| WDK Widgets | 70
Required
Value
Yes
Yes
No
Text prompt for the link. If not specified, the label is used.
No
A parameter to pass to the called page when the link is clicked. You may
have as many or as few <field> tags as you wish for each <link>.
Every <field> must have two children: a <name> with the parameter's
name, and a <value> with the parameter's value.
href
label
prompt
field
<wdk:chart>
Displays numeric data as a chart. You may display the data in several formats (pie chart, bar chart, and so on). In many
cases, you can choose between a "flat" version of a chart and a "2.5-dimensional" version. The 2.5-dimensional version
displays the same data as the flat version, but the chart elements are given a raised, three-dimensional appearance. The
data for the chart can be provided in the widget XML, or you can specify a Java object that contains the chart data.
The chart widget can display the following kinds of charts:
| WDK Widgets | 71
area
Description
An area chart. That is, each data series is displayed as a line, with the area underneath it
colored. If there is more than one data series, the series are stacked, one on top of the other.
The value for any given series is the distance between its line and the line below it.
bar
A bar chart. That is, each data series is displayed as a series of horizontal bars, with the
width of the bar showing the data magnitude.
bar25d
As bar, except that the bars are given a raised, three-dimensional appearance. This is the
default chart type.
column
A column chart. That is, each data series is displayed as a series of vertical columns, with
the height of the column showing the data magnitude.
As column, except that the columns are given a raised, three-dimensional appearance.
column25d
A line chart. That is, each data series is displayed as a horizontal line.
line
pie
A pie chart. That is, a single data series is displayed as portions of a circle. If more than
one data series is passed, the first is displayed, and the others are ignored.
stackedcolumn
stacked~
column25d
A stacked-column chart. That is, each data series is displayed as a single column, made up
of differently-colored regions representing each data point in that series.
As stackedcolumn, except that the columns are given a raised, three-dimensional ap~
pearance.
When Saba builds the chart, it checks to see if the page was passed a parameter with the name <chart_id>_type. If
so, Saba uses the value of that parameter as the chart data type. (As always, if the chart widget's id is not explicitly
| WDK Widgets | 72
specified, Saba uses the chart name as the id. In this case, Saba looks for a parameter with the name
<chart_name>_type.)
If there is no parameter with the appropriate name, Saba checks to see if the widget has a <type> child. If so, Saba
uses the value of that node as the chart type. If the chart is not passed a <chart_id>_type parameter and does not have
a <type> node, Saba displays it as a bar25d chart.
You can also create a <wdk:chartToolbar> widget, which adds controls to let users change the chart's appearance
(for example, by changing the type of chart displayed).
The <wdk:chart> tag has one attribute:
Table 3-20)Attributes of <wdk:chart> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='yourWidgetName']"/>
It has the following child elements, regardless of whether the chart data is specified directly in the XML or with a Java
object:
Table 3-20:)Child Elements of <wdk:chart>Tag
Child Element
Required
Value
No
chartDataExpr
No
id
No
imageMap
If present and set to true, Saba sets the chart to have HTML image
map information. By default, the behavior of an image-mapped chart
is:
When the user hovers the mouse pointer over a chart area, the
browser displays a "tool tip" giving the value of that chart area.
When the user clicks on a chart area, there is no effect.
| WDK Widgets | 73
Child Element
Required
Value
You can change the behavior of clicking on a chart area by using
the imageMapURL child node (described below) or specifying an
image map URL with the ChartData object.
No
type
The type of chart to display. This must be one of the chart types
listed in Table 4-10, "<wdk:chart> Chart Types". Note that this
value is only used if the page is not passed a <chart_id>_type
parameter. If that parameter is passed, the <type> node is ignored.
If the page is not passed a <chart_id>_type parameter and there
is no <type> node, the chart is displayed as a bar25d.
No
wdktags:attachTo
If you do not specify a ChartData object (with the <ChartDataExpr> node), Saba uses the values of the following
child nodes to set the chart data. These nodes are all ignored if there is a ChartData object. Note that some of the
following nodes are listed as "required". They are only required if a ChartData is not specified.
Table 3-20:)Child Elements of <wdk:chart>Tag, ifChartData is not used.
Child Element
imageMapURL
Required
Value
No
The URL to use when generating the imagemap for the chart. If
imageMap i set to true, and imageMapURL is present, Saba
opens this URL when the user clicks anywhere in the chart. The
URL may contain the strings $cat and $ser; Saba replaces these
strings with the name of the category and series for each area of the
chart. Thus, if you set imageMapUrl to
<imageMapURL>javascript:alert(
| WDK Widgets | 74
Child Element
Required
Value
(or not present), imageMapURL is ignored, and clicking in the chart
has no effect.
No
No
The name of the X (independent) axis. This is the axis that contains
the category data points. This is usually the horizontal axis, but in
a bar chart this is the vertical axis, and in a pie chart this would be
represented with a legend of the different pie wedges.
No
The name of the Y (dependent) axis. This axis displays the value of
each category data point. This is usually the vertical axis, but in a
bar chart this is the horizontal axis. In a pie chart, the values of the
dependent axis are represented by the size of the various pie wedges.
No
No
Yes
The list of categories, that is, datapoints on the X axis. This node
has several <category> nodes as children, with each <cat~
egory> containing the name of a datapoint. For example, in a chart
title
xLabel
yLabel
width
height
categories
| WDK Widgets | 75
Child Element
Required
Value
showing sales by quarter, the <categories> node might look
like this:
<categories>
<category>2003: Q1</category>
<category>2003: Q2</category>
<category>2003: Q3</category>
<category>2003: Q4</category>
</categories>
Yes
seriesList
The list of data series. This node contains one or more <series>
nodes, each of which represents a single data series. In turn, each
<series> node contains the following child nodes:
| WDK Widgets | 76
Child Element
Required
Value
egories> node shown above, it might have the following data
series:
<seriesList>
<series>
<name>Northeast</name>
</series>
<series>
<name>West</name>
<data>25000000</data>
<data>22000000</data>
<data>18000000</data>
<data>21000000</data>
</series>
</seriesList>
| WDK Widgets | 77
Child Element
Required
Value
Note: Pie charts display only the first series listed.
No
No
If specified and the chart is a pie type chart then it is used to specify
the label for the "rest" category (that is, the category for values too
small to be shown as individual slices). If not specified, a default
label is printed..
No
If specified and the chart is a pie type chart then it is used to specify
the id for the "rest" category. If not specified, the category is given
the default ID 'rest'
showBorder
restCategoryLabel
restCategoryId
<wdk:chartToolbar>
This widget displays a chart control toolbar. The user can use this toolbar to control what type of chart is used to display
the data, such as changing a bar chart to an area chart.
The toolbar does not link directly to a particular chart. Instead, when the user makes a selection with the chart toolbar,
Saba launches the specified application page, passing the new chart type as a parameter. Any charts on the loaded page
can look for that passed parameter, and use it to set the chart type. For example, if you want the toolbar to control a chart
on the current page, set the toolbar to reload the current page. Any charts on the page could check to see if the toolbar
passed a "chart type" parameter.
The <wdk:chartToolbar> tag has one attribute:
Table 3-20:)Attributes of <wdk:chartToolbar>Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
The current selection of the toolbar. This must be one of the follow~
ing:
column25d
bar25d
| WDK Widgets | 78
Child Element
Required
Value
pie25d
line
area
Yes
No
href
id
No
wdktags:attachTo
Example
This example illustrates how you can use a chartToolbar to control a chart on the same page. Note the following:
When the user makes a selection with the chart toolbar, Saba reloads the current application page.
The chart toolbar's current setting is passed as the out parameter chartType.
| WDK Widgets | 79
The chart examines the value of the chartType parameter to set its type. If the parameter is not passed to the page,
Saba gives it a default value of "column25d".
<?xml version="1.0"?>
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<!-- the head element contains standard header data for all
<wdk:head>
<wdktags:in>
<wdktags:param name="chartType"
defaultValue="column25d"/>
| WDK Widgets | 80
</wdktags:in>
<wdktags:out>
parameter. -->
<wdktags:param name="chartType"/>
</wdktags:out>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:chart name="testedWidget">
<id>chart_test</id>
<type><xsp:expr>chartType</xsp:expr></type>
to set the
| WDK Widgets | 81
data... -->
<chartDataExpr>chartDataObject</chartDataExpr>
</wdk:chart>
name="configuration">
page. -->
<href>chartTest.xml</href>
</wdk:chartToolbar>
</wdk:widgets>
</wdk:page>
</xsp:page>
<wdk:componentPicker>
The <wdk:componentPicker> tag has one attribute:
parameter. -->
| WDK Widgets | 82
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
callbackFn
function(id, dispName,widgetId)
where widgetId is the ID of this widget. This function is passed
to the picker window as the callback parameter. If callbackFn
is not specified, the popup window is passed the default callback
function setData.
The widget does not examine any return value from the function.
Yes
The name of the component associated with this picker. The user is
able to use the picker to select a particular instance of that compon~
ent.
No
If true, users can type directly in the text box instead of launching
the popup window. Defaults to false.
No
No
URL of the picker page to use. If not specified, Saba looks up the
reference in the picker page registry.
No
component
enable
height
href
id
| WDK Widgets | 83
Child Element
Required
Value
If not specified, the value of the widget's name attribute is used.
No
Yes
No
No
No
No
No
No
No
No
input
pageHeader
prompt
size
textValue
value
viewOnly
widthType
height
wdktags:attachTo
| WDK Widgets | 84
<wdk:componentWidget>
This widget displays a component. It generates widgets to display each of the component's attributes, custom fields, and
(if appropriate) security domain. It generates IDs for the child widgets this way:
All the component widget does is it generates a set of widgets representing each attribute, the customfields and the
domain (if applicable). The id of each widget is derived from the id of the component widget as follows:
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
Yes
No
attribute
componentName
displayCustomFields
| WDK Widgets | 85
Child Element
Required
Value
Note: If a component is not cusotmizable (that is, does not im~
plement the ICustomBusinessView interface), then this flag
must be set to false.
No
No
This string is used as the root of the HTML names of the generated
input elements. If <id> is not specified, the widget's name attribute
is used instead.
hasDomain
id
No
onDomainChange
No
Yes
viewOnly
wdktags:attachTo
No
widgetConfig
Any elements that are inside the widgetConfig node are added to
each of the child widgets.
| WDK Widgets | 86
Required
Value
Yes
No
name
readOnly
No
viewOnly
isTextArea
No
reference
No
| WDK Widgets | 87
Required
Value
No
No
No
Variables to submit to the page loaded when this link is clicked. There
may be zero, one, or more field tags. Syntax is the same as for the
<wdk:link> widget's <field> element.
href
mainPage
field
| WDK Widgets | 88
Example
The following displays a simple component (a domain with a name and description attribute):
<wdk:componentWidget name="componentWidget">
<wdktags:attachTo path="parents/leaf"/>
<componentName>Domain</componentName>
/ <attribute>
<name>name</name>
</attribute>
<attribute>
<name>description</name>
</attribute>
<displayCustomFields>true</displayCustomFields>
<hasDomain>false</hasDomain>
</wdk:componentWidget>
The corresponding view page code segment is:
<xsl:for-each select="parents/leaf/wdk:widget[@name='componentWidget']">
<div>
<xsl:call-template name="ComponentWidgetLayout"/>
</div>
| WDK Widgets | 89
</xsl:for-each>
The template named "ComponentWidgetLayout" is in the stylesheet wdk_widgetlayout.xsl, so the view page
must include that stylesheet:
<xsl:import href="../xsl/view/wdk_widgetlayout.xsl"/>
<wdk:customFields>
This creates widgets to let the user view and edit a Saba object's custom fields. It works with the
com.saba.web.custom.CustomFieldXMLProducer object. The CustomFieldXMLProducer object
produces an XML representation of a Saba object's custom fields, and the customFields widget knows how to
interpret that XML representation.
The <wdk:customFields> tag has one attribute:
Table 3-20:)Attributes of <wdk:customFields>Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
wdktags:attachTo
No
id
This string is used as the root of the HTML names of the generated input
elements. If <id> is not specified, the widget's name attribute is used
instead.
| WDK Widgets | 90
Child Element
Required
Value
Note: The WDK engine creates several HTML form elements,
all of which begin with this ID string. You should regard all names
beginning with this string as "reserved" for the rest of the page.
No
The title to display above the custom field input widgets. This is a
header for the entire area (not for a specific column), so you should in~
clude at most one <header> tag.
No
The number of columns to use when displaying the custom fields. The
default is 2.
No
If set to true, the values of the custom fields are displayed as plain
(non-editable) text. Default is "false", unless there is a model object
(as described in "Widgets and Model Objects")
No
A number, indicating the default width (in pixels) separating the columns.
By default, this is 20 pixels.
header
columns
viewOnly
columnPadding
| WDK Widgets | 91
Example
<wdk:customFields name="customFields">
<id>custom</id>
<columns>2</columns>
<header>Custom Fields</header>
</wdk:customFields>
<wdk:dateInput>
The <wdk:dateInput> widget allows users to enter a date. The displayed widget displays both a text field and a
graphic "calendar" element.
| WDK Widgets | 92
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated element. If not specified, the value
of the widget's name attribute is used.
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
If "true", the user can enter a date directly in the text box. (Note that
the widget does not verify that the date is entered in an appropriate
format!)
id
wdktags:attachTo
label
required
prompt
enable
| WDK Widgets | 93
Child Element
Required
Value
If "false", the user must select a date with the calendar widget. The
widget then puts a date string, in the system's default date format, in the
text box.
No
Initial date when the widget is first shown. The widget does not verify
that this text is in an appropriate format.
No
No
No
value
viewOnly
useLabelTerminator
callbackFn
function(dateFieldName,selectedDateString)
The widget does not examine any return value from the function.
No
size
| WDK Widgets | 94
<wdk:dateInput name="abstract">
<required>true</required>
<label>Date</label>
</wdk:dateInput>
<wdk:deleteLink>
This widget generates a "delete" link. It behaves exactly like <wdk:link> (described on ), with the following exceptions:
<field>
<name>actionKey</name>
<value>delete</value>
</field>
| WDK Widgets | 95
The link causes a confirmation window to be displayed. You can specify a text for this window, or use the default
"confirm delete" text.
The <wdk:deleteLink> widget has all the same attributes and subelements as <wdk:link>. It also has the following
additional subelement:
Table 3-20:)Additional Child Elements of <wdk:deleteLink> Tag
Child Element
Required
Value
No
When the user clicks the link, Saba displays a confirmation window.
If <confirmMesg> is specified, that text is used for the confirm~
ation window. Otherwise, a default "confirm delete" message is
used.
confirmMesg
<wdk:domainPicker>
This is a special picker object used to let the user select what domain he is using. It is like the <wdk:sabaPicker>, except
that it automatically presents the user with a list of domains. In addition, as soon as a domain is changed, the main page
(or another URL) is invoked. The user cannot both change a domain and take other actions on the same page. This is
because of the requirement that domain changes be made atomically.
The widget creates two form elements: a text field for displaying the selection, and a hidden field for storing the ID of
the selection. The name of the hidden field is specified by the text in the widget's <id> child tag. If there is no <id>
tag, the widget's name attribute is used. The name of the text field form element is computed by appending the value
of PickerWidget.kTextFieldExtension to the name of the hidden field.
The <wdk:domainPicker> tag has one attribute:
Table 3-20:)Atributes of <wdk:domainPicker> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
| WDK Widgets | 96
callbackFn
Required
Value
No
| WDK Widgets | 97
Child Element
Required
Value
The widget does not examine any return value from the function.
No
If true, users can type directly in the text box instead of launching
the popup window. Defaults to false.
No
No
No
Yes
No
No
No
No
No
No
enable
height
id
label
onPick
prompt
required
textValue
useLabelTerminator
value
| WDK Widgets | 98
Child Element
Required
Value
and Model Objects").
viewOnly
No
No
widthType
wdktags:attachTo
Required
Value
No
No
If present, specifies the Saba application page to open when the do~
main is changed. Either this or <href> must be present.
No
href
mainPage
field
<field><name>actionKey</name>
<value>chgDomain</value></field>
<wdk:file>
The <wdk:file> tag is used to define an input field for uploading file content. This tag must be used within a
<wdk:widgets> tag in a WDK page. The file widget allows the page's user to select a local file on his or her device
and upload it to the server.
The <wdk:file> tag has one attribute:
| WDK Widgets | 99
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated input form element. If not specified,
the value of the name attribute is used.
No
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
id
wdktags:attachTo
maxlength
label
required
viewOnly
useLabelTerminator
The following example illustrates a file field defined within the <wdk:widgets> section of a WDK page:
<wdk:widgets>
<wdk:file name="theFile">
<id>theFile</id>
</wdk:file>
</wdk:widgets>
<wdk:finderQuery>
Displays the condition fields of a finder, letting the user run searches from this page. The contents of the finder may be
specified with a Java Collection, or through a SQL search.
The <wdk:finderQuery> tag has one attribute:
Table 3-20:)Atributes of <wdk:finderQuery> Tag
Attribute Name
name
Required
Value
Yes
The name of the input field. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
No
No
id
wdktags:attachTo
collection
No
sqlMessage
No
driverData
No
presentationData
No
No
resourceBundle
removeCondition
Child Element
Required
Value
No
Yes
Describes what to do when the link is clicked. Must have one and
only one of the following child elements:
addCondition
onAction
No
No
Label for the Reset button. If not present, default label is used.
No
No
Label for the Save Search Query button. If not present, default
label is used. Ignored if <allowSaving> is false or is not
present.
No
No
Label for the Reset Saved Query button. If not present, default
label is used. Ignored if <allowSaving> is false or is not
present.
No
Indicates whether the finder can perform a "blind search", that is,
search with no criteria, thus returning all objects of the relevant
type. Default value is true (finder can perform blind searches).
goLabel
resetLabel
allowSaving
saveQueryLabel
saveQueryDescLabel
resetQueryLabel
allowBlindSearch
Child Element
Required
Value
If set to false, the user must specify at least one search criterion
before running the query.
No
No
No
No
allowBlindSearchPrompt
simpleContext
advanceHref
removeAttribute
Required
Value
No
No
No
If true, field must contain some value before the field is submitted. Defaults
to false.
No
No
No
No
No
No
No
No
id
name
required
label
type
operator
value
size
lovId
component
selectInfo
<className>: Name of the Java Class that generates the drop down menu.
This Java class should implement the com.saba.web.finder.Drop~
Child Element
Required
Value
DownGenerator interface .
Note: The class must have an empty arg constructor. See "Example
3" for an example.
Example 1
In the following example we pass the FinderCollection to both the finderQuery and finderResult widgets.
Note that in the finderQuery widget we also passed presentation data, because the backend finder is an ad hoc finder
without any default driver data or presentation data.
<?xml version="1.0"?>
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.finder.*</xsp:include>
<xsp:include>com.saba.locator.*</xsp:include>
<xsp:include>com.saba.msg.*</xsp:include>
</xsp:structure>
-->
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<!-- the head element contains standard header data for all WDK model pages
-->
<wdk:head>
</wdk:head>
<wdk:form method="POST">
<xsp:logic>
<!-- since this finder does not have default driver data or presentation
data associated with it
-->
<!--
-->
driverData
.addCondition(1,"listl000000000001000",com.saba.permeta.RegularAttributeDatatype.kString);
<!--
-->
-->
condition1.setRequired(false);
<!--
-->
condition1.setLabel("List ID");
presentationData.addPresentationData(condition1);
<!-- now let's get the FinderCollection. Note we only assume that a
Collection is returned.
-->
FinderManager manager =
(FinderManager)getServiceLocator().getManager(Delegates.kFinder);
</xsp:logic>
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:finderQuery name="testedWidget">
<id>fqw</id>
<collection><expr>collection</expr></collection>
<presentationData><expr>presentationData</expr></presentationData>
<onAction>
<href>finders.xml</href>
</onAction>
</wdk:finderQuery>
</wdk:widgets>
</wdk:page>
</xsp:page>
Example 2
This example uses a fully instrumented finder (meta-finder) that has full default information for driver data as well as
presentation data.
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.finder.*</xsp:include>
<xsp:include>com.saba.locator.*</xsp:include>
<xsp:include>com.saba.msg.*</xsp:include>
</xsp:structure>
-->
<!-- the head element contains standard header data for all WDK model pages
-->
<wdk:head>
</wdk:head>
<wdk:form method="POST">
<xsp:logic>
FinderManager manager =
(FinderManager)getServiceLocator().getManager(Delegates.kFinder);
</xsp:logic>
<wdk:model>
<configuration/>
</wdk:model>
</wdk:form>
<wdk:widgets>
<!-- . . . -->
<wdk:finderQuery name="testedWidget">
<wdktags:attachTo path="configuration"/>
<id>portalQuery</id>
<collection><expr>collection</expr></collection>
<onAction>
<href>finders2.xml</href>
</onAction>
</wdk:finderQuery>
</wdk:widgets>
</wdk:page>
</xsp:page>
Example 3
This example illustrates how to generate a drop down menu in the Finder Query widget.
<wdk:finderQuery name="testedWidget">
<wdktags:attachTo path="configuration"/>
<id>portalQuery</id>
<collection>
<expr>collection</expr>
</collection>
<onAction>
<href>finders3.xml</href>
</onAction>
<addCondition>
<id>10</id>
<label>LOV test</label>
<type>kLov</type>
<lovId>listi000000000000905</lovId>
</addCondition>
<addCondition>
<id>11</id>
<type>kSelect</type>
<required>true</required>
<selectInfo>
<className>com.saba.web.finder.SampleDropDownGenerator</className>
<contextName>testing</contextName>
</selectInfo>
</addCondition>
</wdk:finderQuery>
The finder uses a SampleDropDownGenerator object to generate the menu. That object has the following code:
/**
*/
/**
* Empty Constructor
*/
public SampleDropDownGenerator()
/**
* @param locator
*/
throws
SabaException
temp.add("Testing" + i);
temp.add("Testing" + i);
tempList.add(temp);
return tempList;
<wdk:finderResult>
Displays the result of a finder query.
The <wdk:finderResult> tag has one attribute:
Required
Value
Yes
The name of the input field. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated input form element. If not specified,
the value of the name attribute is used.
No
No
No
id
wdktags:attachTo
queryWidgetId
collection
No
sqlMessage
No
driverData
Driver data for the FinderCollection or SQL query. This must contain
a child <expr> element, containing Java code that evaluates to type
DriverData.
If not specified, the appropriate default driver data is used.
Child Element
Required
Value
Note: This need not be specified if there is a <collection>
node that returns a FinderCollection.
No
presentationData
No
No
No
Contains columns to display to the left of the table. Must contain one
<column> tag for each column to display. The <column> tag may
contain widgets, XML data, or <subelement> nodes (described
below).
No
Contains columns to display to the right of the table. Must contain one
<column> tag for each column to display. The <column> tag may
contain widgets, XML data, or <subelement> nodes (described
below).
No
No
Removes a column from the results table. This element must have one
and only one of the following child elements:
customizer
content
leftWidgets
rightWidgets
resourceBundle
removeDisplayColumn
No
addDisplayColumn
Adds a column to the results table. This element must have one and
only one of the following child elements:
Child Element
Required
Value
column either in pixels (<size>40</size>) or as a percentage
(<size>25%</size>).
No
No
If set to true, the page displays all records returned by the search,
instead of breaking the results up into pages. Default is false.
No
No
No
No
If present, removes a condition. This element must have one and only
one of the following child elements:
refresh
showAll
pageSize
statusLabel
noRecordsLabel
removeCondition
No
Yes
Describes what to do when the Next or Prev link is clicked. Must have
one and only one of the following child elements:
addCondition
onAction
Child Element
Required
Value
No
Label for the Next button. Node must have a single <label> node,
containing the label text.
No
Label for the Prev button. Node must have a single <label> node,
containing the label text.
No
No
If true, provide an Export link to let users export table to CSV format.
If false, do not provide this link. Defaults to true.
No
Specified that a column can be used to sort the table. This node must
contain the column's ID (for a fixed column) or name (for a configurable
column). You may have as many or as few <sort> elements as you
wish.
No
If present, creates an Add button letting users add objects of the type
shown by the finder, and specifies what page should be launched by
the Add button. For details about the onAdd widget's contents, see
below.
No
If true, creates a Modify Table link that the user can click to modify
the table view. Defaults to false.
No
Label for the Modify Table button. If not present, default label is used.
No
No
next
prev
export
sort
onAdd
modifyTable
modifyTableLabel
resetPosition
removeAttribute
Required
Value
Yes
Contains the data to display in this column. May have a @type attrib~
ute, described below.
No
data
label
type
Required
Value
No
Type of data to display in the column. This affects how the column is
formatted. Possible values are:
If the data type is checkbox, the <data> node has the following child elements:
Required
Value
No
If true, display a Check All box in the column header. Users can
click that box to check or uncheck all columns. Default is false.
Yes
Yes
checkAll
id
value
Required
Value
No
If specified (and no label tag is specified) then the display name of the
component is used to autogenerate the label of the link.
No
Indicates what type of action is used to create the object. This is used
in combination with component to generate the label for the button,
if ?the onAdd does not have label or prompt tags. Appropriate
values are create (default) or add. If a label is not specified, the
application generates an appropriate label from this and the component's
display name (for example, "Add Course", "Create Goal").
component
actionType
Example 1
In this example the finder result widget displays the column explicitly specified in the widget declaration:
<wdk:finderResult name="result">
<export>false</export>
<print>false</print>
<queryWidgetId>query</queryWidgetId>
<sqlMessage>PlatformFinder.kGeneratorTestObjectMetaFinder</sqlMessage>
<driverData>
<expr>driverData</expr>
</driverData>
<presentationData>
<expr>presentationData</expr>
</presentationData>
<addDisplayColumn>
<name>name</name>
<label>
<wdktags:i18n.componentAttribute component="GeneratorTestObject"
attribute="name"/>
</label>
<size>80%</size>
</addDisplayColumn>
<onAction>
<href>pickGeneratorTestObject.xml</href>
</onAction>
</wdk:finderResult>
Note that the <queryWidgetId> tag is used to identify the source of the driver data values coming from a query
widget. (In the example above the <driverData> and <presentationData> are used to make sure the default
values are not used.)
Example 2
In this example a left widget column is added to the result:
<wdk:finderResult name="result">
<queryWidgetId>query</queryWidgetId>
<sqlMessage>PlatformFinder.kGeneratorTestObjectMetaFinder</sqlMessage>
<driverData>
<expr>driverData</expr>
</driverData>
<presentationData>
<expr>presentationData</expr>
</presentationData>
<leftWidgets>
<column>
<data type="icon">
<wdk:link name="pick">
<icon>
</icon>
<action>
<type>process</type>
<function>
<name>window.opener.<xsp:expr>callback</xsp:expr>
</name>
<arg type="String">
<wdk:display>
<id>0</id>
</wdk:display>
</arg>
<arg type="String">
<wdk:display>
<name>name</name>
</arg>
<arg type="String">
<xsp:expr>formfield</xsp:expr>
</arg>
</function>
</action>
<action>
<type>close</type>
</action>
</wdk:link>
</data>
</column>
</leftWidgets>
<addDisplayColumn>
<name>name</name>
<label>
<wdktags:i18n.componentAttribute component="GeneratorTestObject"
attribute="name"/>
</label>
<size>80%</size>
</addDisplayColumn>
<onAction>
<href>pickGeneratorTestObject.xml</href>
</onAction>
Example 3
In this example the content of the table is overridden. Instead of text, a link is displayed.
<wdk:finderResult name="result">
<sqlMessage>PlatformFinder.kGeneratorTestObjectMetaFinder</sqlMessage>
<driverData>
<expr>driverData</expr>
</driverData>
<presentationData>
<expr>presentationData</expr>
</presentationData>
<leftWidgets>
<column>
<data type="icon">
<wdk:link name="pick">
<icon>
<wdktags:i18n.path bundle="common"
name="kI18nXXXXiconPlus"/>
</icon>
<action>
<type>process</type>
<function>
<name>window.opener.<xsp:expr>callback</xsp:expr></name>
<arg type="String">
<wdk:display>
<id>0</id>
</wdk:display>
</arg>
<arg type="String">
<wdk:display>
<name>name</name>
</wdk:display>
</arg>
<arg type="String">
<xsp:expr>formfield</xsp:expr>
</arg>
</function>
</action>
<action>
<type>close</type>
</action>
</wdk:link>
</data>
</column>
</leftWidgets>
<addDisplayColumn>
<name>name</name>
<label>
<wdktags:i18n.componentAttribute
component="GeneratorTestObject" attribute="name"/>
</label>
<wdk:finderResult name="result">
<queryWidgetId>portalQuery</queryWidgetId>
<sqlMessage>PlatformFinder.kPortalMetaFinder</sqlMessage>
<removeDisplayColumn>
<name>pageManagerClass</name>
</removeDisplayColumn>
<leftWidgets>
<column >
<data type="checkbox">
<checkAll>true</checkAll>
<id>checkBoxId</id>
<value>
<wdk:display>
<id>0</id>
</wdk:display>
</value>
</data>
</column>
</leftWidgets>
<rightWidgets>
<column>
<data type="icon">
<wdk:frameworkLink name="delete">
<icon><wdktags:i18n.path name="kI18nXXXXDeleteGif1"
bundle="common"/></icon>
<action>
<type>process</type>
<function>
<name>confirm</name>
<arg type="String">
<wdktags:i18n.label bundle="common"
name="kI18nXXXXDeleteConfirmation"/>
</arg>
</function>
</action>
<mainPage>findPortal.xml</mainPage>
<field>
<name>actionKey</name>
<value>delete</value>
</field>
<field>
<name>id</name>
<value>
<id>0</id>
</wdk:display>
</value>
</field>
<prompt><wdktags:i18n.label bundle="common"
name="kI18n5006DeletePrompt"/></prompt>
</wdk:frameworkLink> </data> </column> </rightWidgets> <xsp:logic> if (actionKey.equals("delete")) {
<refresh>true</refresh> } </xsp:logic> <onAction> <mainPage>findPortal.xml</mainPage>
</onAction></wdk:finderResult>
All checkboxes under first column have same name, "checkBoxId", and the value submitted with each checkbox is the
id column of the row. To use the value submitted by the checkbox, use the following code:
quired="false"
Example 5
Adds sort columns by specifying the <sort> tag.
<wdk:finderResult name="result">
<queryWidgetId>query</queryWidgetId>
<sqlMessage>PlatformFinder.kPortletMetaFinder</sqlMessage>
<removeDisplayColumn>
<name>pageManagerClass</name>
</removeDisplayColumn>
<leftWidgets>
<column>
<data type="icon">
<wdk:frameworkLink name="edit">
<mainPage>editPortlet.xml</mainPage>
<field>
<name>actionKey</name>
<value>edit</value>
</field>
<field>
<name>id</name>
<value>
<wdk:display>
<id>0</id>
</wdk:display>
</value>
</field>
<prompt><wdktags:i18n.label bundle="common"
name="kI18n5006EditPrompt"/></prompt>
</wdk:frameworkLink>
</data>
</column>
</leftWidgets>
<rightWidgets>
<column>
<data type="icon">
<wdk:frameworkLink name="delete">
<icon><wdktags:i18n.path name="kI18nXXXXDeleteGif1"
bundle="common"/></icon>
<action>
<type>process</type>
<function>
<name>confirm</name>
<arg type="String">
</arg>
</function>
</action>
<mainPage>findPortlet.xml</mainPage>
<field>
<name>actionKey</name>
<value>delete</value>
</field>
<field>
<name>id</name>
<value>
<wdk:display>
<id>0</id>
</wdk:display>
</value>
</field>
<prompt><wdktags:i18n.label bundle="common"
name="kI18n5006DeletePrompt"/></prompt>
</wdk:frameworkLink>
</data>
</column>
</rightWidgets>
<xsp:logic>
if (actionKey.equals("delete")) {
<refresh>true</refresh>
</xsp:logic>
<onAction>
<mainPage>findPortlet.xml</mainPage>
</onAction>
<sort>name</sort>
<sort>type</sort>
</wdk:finderResult>
Example 6
Using FinderCollections: Fully instrumented metadata finder
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.finder.*</xsp:include>
<xsp:include>com.saba.locator.*</xsp:include>
<xsp:include>com.saba.msg.*</xsp:include>
</xsp:structure>
-->
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<!-- the head element contains standard header data for all WDK model pages
-->
<wdk:head>
</wdk:head>
<wdk:form method="POST">
<xsp:logic>
FinderManager manager =
(FinderManager)getServiceLocator().getManager(Delegates.kFinder);
</xsp:logic>
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:finderQuery name="testedWidget">
<wdktags:attachTo path="configuration"/>
<id>portalQuery</id>
<collection><expr>collection</expr></collection>
<onAction>
<href>finders2.xml</href>
</onAction>
</wdk:finderQuery>
<wdk:finderResult name="testedWidget">
<wdktags:attachTo path="configuration"/>
<id>portalResult</id>
<queryWidgetId>portalQuery</queryWidgetId>
<collection><expr>collection</expr></collection>
<onAction>
<href>finders2.xml</href>
</onAction>
</wdk:finderResult>
<wdk:parameters name="parameters">
<wdktags:attachTo path=""/>
e </wdk:widgets>
</wdk:page>
</xsp:page>
Example 7
Adds label to the leftWidget/rightWidget nodes.
<wdk:finderResult name=
"testedWidget">
<leftWidgets>
<column>
<label>Testing Lable</label>
<wdk:link>
<icon>
<wdktags:i18n.path
name="kI18nIconCalendarImg" bundle="common_images"/>
</icon>
</wdk:link>
</data>
</column>
</leftWidgets>
<rightWidgets>
<column>
<label>Testing Lable333</label>
<wdk:link>
<icon>
<wdktags:i18n.path
name="kI18nIconDeleteImg" bundle="common_images"/>
</icon>
</wdk:link>
<wdk:link>
<icon>
<wdktags:i18n.path
name="kI18nIconEditImg" bundle="common_images"/>
</icon>
</wdk:link>
</data>
</column>
</rightWidgets>
</wdk:finderResult>
<wdk:finderControl>
Displays the controls (Previous and Next buttons) for a finder iterator. This widget has the following attributes:
Table 3-20:)Atributes of <wdk:finderControl> Tag
Attribute Name
name
Required
Value
Yes
The name of the input field. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated input form element. If not specified,
the value of the name attribute is used.
No
yes
Yes
No
No
Text to display how many records were found. If not present, default
text is used.
No
Yes
Describes what to do when the link is clicked. Must have one and only
one of the following child elements:
id
wdktags:attachTo
finderIteratorExpr
finderControlExpr
pageSize
statusLabel
noRecordsLabel
onAction
No
Label for the Next button. Node must have a single <label> node,
containing the label text.
No
Label for the Prev button. Node must have a single <label> node,
containing the label text.
<next>
<prev>
<wdk:form>
The <wdk:form> tag is used to define the main WDK form in a WDK page. WDK Forms are the standard widget
used to submit information from a page back to the server. Each Form has a corresponding JavaScript function for
submission of the form and may have multiple hidden fields.
Note: There must be only one <wdk:form> definition per page.
A WDK Form may have the following child elements only:
When processed, this tag generates an HTML form with a JavaScript function to submit the form to the server, for
example:
<script language="JavaScript">
function wdkSubmitForm(url) {
document.theForm.action=url;
document.theForm.submit();
</script>
This <wdk:form> tag has the following attributes:
Table 3-20:)Atributes of <wdk:form> Tag
Attribute Name
Required
Value
No
Specifies whether the form uses HTTP "POST" or "GET". The default is
"GET".
No
Specifies the MIME type for the form's submission. This must be either:
method
enctype
Attribute Name
Required
Value
or
"multipart/form-data" (for uploading files)
<wdk:frameworkLink>
This special-purpose widget creates a link to the start page for the desktop. In addition, it passes the following field to
that page:
clientMainPresentation: The currently active value (present in the request object at run time) is passed.
If no child elements are specfied, then this link generates a field parameter as if the developer had typed the following
child element into a particular link specification on a model page:
<field>
<name>clientMainPresentation</name>
<value>currentlyActiveValue</value>
</field>
The value element above use the currently active values present in the request object at run time.
The widget tag can also explicitly specify the value to pass as clientMainPresentation, by declaring a <field>
child of the <wdk:frameworkLink> widget.
This widget's attributes, and child tags, are exactly like those of <wdk:link> (described), with the following exception:
Any <action> tags that attempt to launch a new page-such as submit or sabapage actions-is ignored. Instead,
after the last <action> is run, the start page is launched.
The <wdk:frameworkLink> widget provides one child tag in addition to those defined by <wdk:link>:
Table 3-20:)Child Elements of <wdk:frameworkLink> Tag
Child Element
mainPage
Required
Value
No
If provided, this attribute specifies the URL of the main page for this
site. If not provided, the widget uses the value returned by re~
quest.getParameter( "clientMainPresentation").
The simple examples below show how the widget can be used. There are many additional attributes and elements that
are not shown here. Please see the <wdk:link> section (on page ) for additional information.
<label>No Fields</label>
</wdk:frameworkLink>
<field>
<name>Age</name>
<value>75</value>
</field>
<label>One Field</label>
</wdk:frameworkLink>
<mainPage>/sysadmin/security/domain/domainMain.saba</mainPage>
<label>Main page</label>
</wdk:frameworkLink>
<mainPage>/sysadmin/security/domain/domainMain.saba</mainPage>
<field>
<name>Age</name>
<value>75</value>
</field>
</wdk:frameworkLink>
<wdk:functionality>
This widget enables you to mark certain areas of a page as being connected with an area of Saba functionality. If the
functionality is turned on (through the Saba Functionality On/Off screen), certain widgets are executed. If it is turned
off, other widgets are executed.
The <wdk:functionality> tag has one attribute:
Table 3-20:)Atributes of <wdk:functionality> Tag
Attribute Name
Required
Value
Yes
name
Required
Value
No
Contains one or more widgets. These widgets are executed if the func~
tionality specified by "name" is turned on.
No
Contains one or more widgets. These widgets are executed if the func~
tionality specified by "name" is turned off.
on
off
Note: You can nest one functionality area inside another. However, these must be for two different functional
areas. For example, you might nest a "learning" functionality widget inside a "planning" functionality. If you try
to nest a "learning" functionality widget inside another "learning" functionality, it causes a page compilation error.
Example
This code displays different text depending on whether or not the "Planning" functionality is enabled:
<wdk:functionality name="Functionality.Plan">
<on>
<wdk:pageText name="testWidget">
<resource>kPlanFunctionalityOn</resource>
</wdk:pageText>
</on>
<off>
<wdk:pageText name="testWidget">
<resource>kPlanFunctionalityOff</resource>
</wdk:pageText>
</off>
</wdk:functionality>
<wdk:genericText>
The <wdk:genericText> widget creates a text entry field. Depending on the maximum size and threshold values
specified, Saba creates either a single-line input box or a multi-line text area.
The <wdk:genericText> tag has one attribute:
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
No
This id is the name of the generated element. If not specified, the value
of the widget's name attribute is used.
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
No
No
No
height
id
label
maxLength
required
threshold
useLabelTerminator
viewOnly
wdktags:attachTo
width
<wdk:hiddenField>
The <wdk:hiddenField> tag is used to define hidden fields within a WDK form. For each hidden field defined
within the form, a JavaScript function is generated that sets the value of the field in the main form.
<script language="JavaScript">
document.WDK_Main_Form.<xsl:value-of
select="name"/>.value=value;
</script>
Note: Usually, you should define output parameters by using the <wdktags:out> section, as described in
"Declaring In/Out Parameters""Declaring In/Out Parameters" on page 3-11. However, in some special circumstances
you may find it useful to declare a hidden field.
The <wdk:hiddenField> tag has the following child elements:
Table 4-49:)Child Elements of <wdk:hiddenField> Tag
Attribute Name
Required
Value
Yes
No
name
value
The following code fragment demonstrates the complete definition of a <wdk:form> element in the page
view_domain.xml. In this example, the WDK form contains one hidden field and a WDK model that is constructed
using the <wdktags:execute> tag.
<wdk:form method="GET">
<wdk:hiddenField>
<name>do
mainID</name>
<value><xsp:expr>domainId</xsp:expr></value>
</wdk:hiddenField>
<wdk:model>
<wdktags:execute
manager="com.Saba.client.domain.DomainCommandManager"
command="getDomainAndParents" argument="domainId"/>
</wdk:model>
</wdk:form>
<wdk:impresence>
The <wdk:impresence> widget is used to display information about a user's Instant Messenger accounts. The widget
is displayed as an icon and a clickable link. If the user clicks the link, the widget opens a popup window showing
information about a target user's instant messenger accounts. If the target user and the source user both have accounts
on the same service, the widget displays the target user's account on that service as a clickable hyperlink. The user can
click the link to open an IM chat window with the target user.
The <wdk:impresence> tag has one attribute:
Table 4-50:)Attributes of <wdk:impresence> Tag
Attribute Name
Required
Value
Yes
The name of the input field. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
No
Saba ID of the source user. (This is used to determine which of the tar~
get's instant-messenger accounts should be displayed as hyperlinks.)
Default value is the currently logged-in user.
No
Icon to click to open the pop-up window showing the instant messenger
information. If not specified, a default icon is used.
No
targetUserId
srcUserId
icon
prompt
<wdk:input>
The <wdk:input> tag is used to define a single-line text input field within a page. This tag must be used within a
<wdk:widgets> tag in a WDK page. The input widget allows users to enter small amounts of text (in HTML it
corresponds to the input form element of text or password type).
The <wdk:input> tag has one attribute:
name
Required
Value
Yes
The name of the input field. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This ID is the name of the generated input form element. If not specified,
the name attribute is used.
No
No
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
If true, the input field cannot be edited. (Certain UIs may indicate this
in various ways. For example, a read-only input field might have a gray
background.) Defaults to false.
No
No
No
The rough size of the input field. Must be one of the following:
id
wdktags:attachTo
maxlength
value
label
required
password
readonly
disabled
size
sizeType
Child Element
Required
Value
No
No
No
viewOnly
useLabelTerminator
number
Required
Value
Yes
Specifies what types of numbers can be entered with the widget. Must
be one of the following:
type
No
No
The name of a form element to copy the value from. A <field> must
have either a <value> or a <formfield> child, but not both.
alert
formField
The following example illustrates a variety of input fields defined within the <wdk:widgets> section of a WDK
page. Each input field is dynamically attached to a Node in the model.
<wdk:widgets>
<wdk:input name="param">
<wdktags:attachTo path="/info_service/param"/>
<id><wdktags:nodeRef path="name"/></id>
<value><wdktags:nodeRef path="value"/></value>
<size>45</size>
<sizeType>large</sizeType>
</wdk:input>
<wdk:input name="title">
<wdktags:attachTo path="/info_service"/>
<value><wdktags:nodeRef path="title"/></value>
</wdk:input>
<wdk:input name="username">
<wdktags:attachTo path="/info_service"/>
<value><wdktags:nodeRef path="username"/></value>
</wdk:input>
<wdk:input name="password">
<wdktags:attachTo path="/info_service"/>
<value><wdktags:nodeRef path="password"/></value>
<sizeType>small</sizeType>
</wdk:input>
<wdk:input name="pos_int">
<wdktags:attachTo path="/info_service"/>
<value><wdktags:nodeRef path="emp_number"/></value>
<number>
<type>kPositiveInt</type>
message. -->
<alert>
<wdktags:i18n.label name="kBadNumberEntered"/>
</alert>
</number>
</wdk:input>
</wdk:widgets>
<wdk:impresence>
This widget displays a link that launches a popup window, which displays information about a particular user's instant
messaging handles. The window also displays links to open an instant-messaging conversation with that user, if
appropriate. (These links are only shown if the logged-in user and the targetted user have accounts on the same instant
messaging services.)
The <wdk:impresence> tag has one attribute:
Table 4-54:)Attributes of <wdk:impresence> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
No
The user ID of the person who is contacting the target. (This is used to
determine which IM services the users have in common.) By default,
this is the logged-in user.
No
The icon to display for launching the pop-up window. If not specified,
a default icon is used.
No
Prompt to display for the icon. By default, the target user's username is
used.
targetUserId
srcUserId
icon
prompt
Example
The following code in a model page defines the widget:
<wdk:impresence name="imtest">
<tr>
<td>
<xsl:apply-templates
select="$wdkWidget[@name='imtest']"/>
</td>
</tr>
<wdk:link>
The <wdk:link> tag is used to define a link within a page. This tag must be used within a <wdk:widgets> tag in
a WDK page.
This tag generates JavaScript code for activating the link. A <wdk:link> widget can be used to perform a set of
actions, such as:
This generated script calls the appropriate functions based on the set of actions.
A link tag can open a Saba application page. It can do this in several ways:
Some pages aunch themselves, but with different command options. For example, an application page might let people
sort a table by various columns. When the user clicks a column heading, the page launches itself, with a parameter
specifying which column the page should be sorted by.
The problem is, an application page is uniquely specified by its control page (which, in turn, specifies the model and
view pages, the widget library, and so on.). However, the link widget is specified on the model page, and the model
page's code cannot know which control page might launch it (since a single model page can be used by several control
pages). For this reason, the WDK permits the <wdk:link> tag to specify a model page. In this case, the control page
must contain a <wdk:links> section that maps the model-page link to a link to a control page (as described in "Control
File""Control File" on page 3-5).
The <wdk:link> tag has one attribute:
Table 4-56:)Attributes of <wdk:link> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name (in the target code, for example, HTML) of the
generated link. If not specified, the value of the name attribute is used.
No
No
No
If the link is displayed as an icon, the label text (if any) is shown next
to it. If it is displayed as a hyperlink, the label text is used as the link.
No
No
No
Variables to submit to the page loaded when this link is clicked. There
may be zero, one, or more field tags.
No
id
wdktags:attachTo
prompt
label
icon
type
field
do
<do>action_name</do>
has precisely the same effect as
<field>
<name>actionKey</name>
<value>action_name</value>
</field>
Note: The fields are updated, and any popup window is launched
(and its actions run), BEFORE any of the actions are run.
Child Element
Required
Value
Actions are executed in document order until either a submit,
sabapage, or close action is called, or the function of a process
action returns false.
Note: An <action> tag is not required if there is an <href>
tag belonging to the <wdk:link>, or if there is a <popup> tag.
No
Equivalent to declaring
href
<action type="submit">
<href>[tag contents]</href>
</action>
as the last action for this link. (All <action> tags are executed, in or~
der, before the <href> tag is executed.)
No
No
The window used to open the link (used as the value of the HTML
target attribute). If omitted, the target is the current window.
No
Specifies the minimum width (in pixels) to allocate for the link widget.
The widget is at least this wide, even if the label text doesn't require that
much space.
popup
target
minWidth
None of the child elements of <wdk:link> have any attributes. However, the field, action, and popup child
elements can contain subelements, as described below.
<field> Child Element
The <field> element is used to specify HTTP parameters that should be passed to the page being launched. It has the
following subelements:
Required
Value
Yes
The name of the HTTP parameter. This does not have to correspond to
a Java page variable.
No
The text to use as the field's value. A <field> must have either a
<value> or a <formfield> child, but not both.
No
The name of a form element to copy the value from. A <field> must
have either a <value> or a <formfield> child, but not both.
name
value
formField
type
Required
Value
No
Child Element
Required
Value
href
<function>
This tag is required if the <type> is process. The tag has the follow~
ing subtags:
Required
Value
No
This has the same syntax and behavior as an <action> tag that belongs
to <wdk:link> (described in "<action> Child Element").
An <action> that belongs to a <popup> must be of type process,
sabapage, or submit. (sabapage and submit tags load a page
into the popup window, not the original window.) You can use process
actions to perform any operations necessary before launching the popup
window. (If any of the process actions returns "false", the entire
link aborts: no further popup actions are run, the popup window is not
opened, and the actions that belong directly to the <wdk:link> tag
does not run.)
action
Note: The action tag is required unless the <popup> tag has
an <href> child.
No
Equivalent to declaring
href
<action type="submit">
<href>[tag contents]</href>
</action>
as the last action for this popup. (All <action> tags are executed, in
order, before the <href> tag is executed.)
No
input
No
output
Specifies a value returned by the popup window. When the popup win~
dow executes its <callback> action, each of the popup's output values
is available to the original page. It can use them if it declares correspond~
ing <output> tags here.
Each <output> tag must have a <name> child and a <formField>
child:
Child Element
Required
Value
No
Name of the popup window. This is displayed in the window's title bar.
No
No
windowTitle
window_name
windowFeatures
<wdk:link name="payrollSite">
<action>
<href usesite="false">http://payroll.my-corp.com/</href>
</action>
<target>payroll_window</target>
</wdk:link>
This is translated into HTML code along these lines:
a fixed value, and the other's is the value of a Java expression. When the user clicks the link, a WDK page is loaded,
and is passed the specified values.
<wdk:link name="del e
teButton">
<wdktags:attachTo t
h="wdk:collection/info_service"/>
<field>
<name>id</name>
<value><wdktags:nodeRef path="id"/></value>
</field>
<field>
<name>actionKey</name>
<value>delete</value>
</field>
<field>
<name>userId</name>
<value><xsp:expr>user.getId()</xsp:expr></value>
</field>
<action> <!-- Only one action: load the specified page -->
<href>iss_view_info_services.xml</href>
</action>
<!-- We load the label and prompt from a localized resource; see
<label><wdktags:i18n.label name="kI18n9000Delete"/></label>
<prompt><wdktags:i18n.label name="kI18n9000Delete"/></prompt>
</wdk:link>
Note that this page links to a WDK model page. The control page for this source page must map the link to a link to a
control page. It has code something like this:
<wdk:links>
<wdk:link model="iss_view_info_services.xml"
control="iss_view_info_services.rdf"/>
</wdk:links>
This indicates that every model page link to the page iss_view_info_services.xml should be treated as a link
to the control page iss_view_info_services.rdf.
When the user clicks on the delete button, the source page launches the target WDK page. As always, the source's out
variables are passed to the target page as parameters. In addition, the target is passed three parameters specified by this
link:
id: The value passed is the value in the source page's wdk:collection/info_service/id node.
actionKey: The value "delete" is passed.
userId: When the source page is built by the WDK engine, it executes the Java code within the <xsp:expr>
tag, and replaces the tag with the code's output. The resulting text (say, "fbaggins") is used as the value of the
userId parameter.
The target page, presumably, has input parameters named "id", "actionKey", and "userId", which it uses to
access the passed values.
Example 3: Executing JavaScript Before Loading Page
In this example, when the link is clicked on, it first executes a JavaScript function. This function (whose code is not
shown here) displays a confirmation dialogue to the user. If the user clicks "OK", the link continues to its next action,
loading a successor WDK page. If the user clicks "Cancel", the function returns "false", and the link stops executing
code-it does not open the successor page.
<wdk:link name="confirmBeforeSubmit">
<label>Submit</label>
<pr
<action>
<function>
</function>
</action>
<!-- If the process does not return "false", the next action is
executed. -->
<action>
<href>nextPage.xml</href>
</action>
</wdk:link>
As with example 2, the source's control page has to map the nextPage.xml link (a link to a model page) to a link to
an appropriate control page.
Example 4: Link Opening a Popup Window
In this example, the link opens a popup window, passing the value of the source page's client_bunit field. The
popup window launches a WDK page, which presumably has its own link object, containing a callback action. When
the user activates the link in the popup window, the value of its output business_unit variable is copied to the
source page's client_bunit field.
<wdk:link name="open_popup1">
abel>
<popup>
<name>business_unit</name>
<formField>client_bunit</formField>
</input>
<name>business_unit</name>
<formField>client_bunit</formField>
</output>
<action>
<href>wdkBusinessUnitPicker.xml</href>
</action>
</popup>
<!-- After the popup window calls its callback function, the
<action>
<type>submit</type>
<href>nextPage.xml</href>
</action>
</wdk:link>
As with the other examples, the source control page must map the "model" (.xml) links to links to control pages (.rdf).
The page loaded by the popup window presumably has a link object of its own, along these lines:
<wdk:link name="done">
<label>Done</label>
<action>
<type>callback</type>
</action>
<action> <!-- Close the popup window when the user clicks "done" -->
<type>close</type>
</wdk:link>
The above link calls the auto-generated callback function in the main window (copying the output variable from the
popup window to the main window's client_bunit field), then close the popup window.
<wdk:list>
This widget generates a list of selectable items. The widget can present these items as a set of radio-buttons, a set of
checkboxes, or a selectable list of entries. The list is set up as an HTML form element. When the user launches a new
application page (by clicking on a link), the selected elements in the list is passed to the new page as HTML parameters.
Each entry in the list is identified by an <option> tag. When the page is transformed by the WDK engine, each of
these tags is automatically wrapped inside a <wdk:widget> tag so a view sheet can easily customize the look and
layout of each option. The name attribute of the <wdk:widget> tag for the option is the name attribute of the original
<wdk:widget> tag + "_option".
The <wdk:list> tag has one attribute:
Table 4-61:)Attributes of <wdk:list> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated element. If not specified, the value
of the widget's name attribute is used.
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
Indicates what type of list this is. Available list types are "select",
"radio", and "checkbox".
No
No
Entries in the list. Each option tag must have two children:
id
wdktags:attachTo
label
required
type
size
option
No
selection
Indicates which option or options are selected when the application page
is first loaded. The <selection> tag has one or more <value>
subelements. These <value> tags contain text corresponding to <op~
Child Element
Required
Value
tion><value> text. The options whose values are listed here are se~
lected when the page is loaded.
No
Defines an action or actions to take when the user changes the selection
in the list. This is described further in "List Events".
No
No
No
Valid values are "left" or "right". Indicates whether the item labels
(that is, the <text> elements within each <option> element) should
be printed on the left or right side of the checkboxes or radio buttons.
The default is "left" (labels printed to the left of the buttons).
No
Only applies to "select" lists. If true (default), show a line for "select
none". If false, only show lines for the list values you provide, forcing
the user to pick one.
event
viewOnly
useLabelTerminator
labelOrder
showBlank
List Events
A list can define a series of actions to take when the user changes the selection in the list. The list does this by defining
an <event> tag with this format:
<wdk:list name="language_list">
<id>language</id>
<type>radio</type>
<option>
<value>en_US</value>
</option>
<option>
<value>de_DE</value>
<text>German</text>
</option>
<option>
<value>fr_FR</value>
<text>French</text>
</option>
<selection>
</selection>
</wdk:list>
The list is displayed as a radio list with three options: "English", "German", and "French". ("English" is selected when
the page loads.) The user can change the selection by clicking on any of the radio buttons. When the user leaves the
page (by clicking on a link), the selected value is passed to the next page as an HTML parameter-the key is "language"
(the name of the list), and the value is the value associated with whichever list entry is selected. (For example, if the
user selects "German" then loads a new page, the new page is passed an HTML parameter named "language" with
the value "de_DE".)
Example: Dynamically Generated List
This list's contents are set dynamically, based on the contents of the model portion of the page. This list displays the
information service templates for the system. The list is attached to the model, which in this case contains the list of
known templates. The <wdktags:repeat> tag is used to iterate over the templates and creating an option for each,
building the options in the list.
<wdk:list name="templateSelector">
<type>select</type>
<option>
wdk:main/info_service_template/name -->
<value>
<wdktags:nodeRef path="name"
source="templateOption"/>
</value>
<text>
<wdktags:nodeRef path="name"
source="templateOption"/>
</text>
</option>
</wdktags:repeat>
<selection>
<value><xsp:expr>defaultSelection</xsp:expr></value>
</selection>
</wdk:list>
<wdk:lovPicker>
The <wdk:lovPicker> can be used to place a list-of-values (LOV) picker (a combination of a dropdown menu, a
link, and a popup window displaying a finder) on a page. That is, the page containing a sabaPicker displays a
dropdown menu and a link. When the user activates the link, the popup window containing the finder is launched. (It is
much like a <wdk:sabaPicker>, except that it displays a drop-down menu instead of a text entry field.
The widget creates two form elements: a drop-down menu for choosing the selection, and a hidden field for storing the
ID of the selection. The name of the hidden field is specified by the text in the widget's <id> child tag. If there is no
<id> tag, the widget's name attribute is used.
The <wdk:lovPicker> tag has one attribute:
Table 4-64:)Attributes of <wdk:lovPicker> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
callbackFn
Required
Value
No
Child Element
Required
Value
The widget does not examine any return value from the function.
No
If true, users can type directly in the text box instead of launching
the popup window. Defaults to false.
No
No
No
No
No
Initial value of the selection. Defaults to the empty string (that is,
the first element in the list).
No
No
No
No
No
enable
id
label
prompt
required
value
useLabelTerminator
value
viewOnly
widthType
Child Element
Required
Value
and 600 (inclusive). Defaults to 300.
height
No
wdktags:attachTo
<wdk:multistep>
This widget is used to create a multi-step application page. The widget displays any of several smaller model files within
itself. The user can move from one page to the next in order. This enables you to build "wizard"-style pages where users
follow a sequence of steps.
The <wdk:multistep> tag has one attribute:
Table 4-65:)Attributes of <wdk:multistep> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
If true, the user must follow the steps in order. The widget displays
Next and Previous buttons to let the user advance to the next stage or
go back to the previous one, but users cannot jump to any arbitrary step.
forced
If false (the default setting), the user can follow the steps in order,
but can also jump ahead or behind to any other step.
No
showButton
Child Element
Required
Value
If false, the widget displays Next and Previous buttons if <forced>
is true, but not if <forced> is false.
Yes
Yes
selected
action
Required
Value
Yes
ID for this step. This is used by the <selected> node to identify the
currently active step.
Yes
Yes
No
A parameter to pass to the called page when the link is clicked. You may
have as many or as few <field> tags as you wish for each <link>.
Every <field> must have two children: a <name> with the parameter's
name, and a <value> with the parameter's value.
id
label
href
field
<wdk:multisection>
This widget is used to create a multi-sectionp application page. The widget displays any of several smaller model files
within itself, each as a tabbed section. The user can jump to whichever tab he or she wants.
The <wdk:multisection> tag has one attribute:
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
Yes
selected
action
Required
Value
Yes
ID for this step. This is used by the <selected> node to identify the
currently active step.
Yes
Yes
No
A parameter to pass to the called page when the link is clicked. You may
have as many or as few <field> tags as you wish for each <link>.
Every <field> must have two children: a <name> with the parameter's
name, and a <value> with the parameter's value.
id
label
href
field
<wdk:pageText>
This widget is used to include page-specific text (such as page instructions) from the page text resource bundle.
The <wdk:pageText> tag has one attribute:
Table 4-72:)Attributes of <wdk:pageText> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
No
This id is the name (in the target code, for example, HTML) of the frame
containing the included page. If not specified, the value of the widget's
name attribute is used.
Yes
The resource whose value should be included in the page. You may have
several <resource> tags in the widget. The text of those resources is
automatically separated by <BR /> tags.
No
A bind variable needed by the resource. You may have several <bind>
tags if necessary.
wdktags:attachTo
id
resource
bind
Example
<wdk:pageText name="pageText">
<wdktags:attachTo path="."/>
<id>date_test</id>
<resource>kTeamLearningHistory1</resource>
<resource>kTeamLearningHistory2</resource>
</wdk:pageText>
<wdk:pageTitle>
This widget specifies several properties for the application page. It specifies the page's title, what Saba component is
used by the page, and several other properties of the page (such as whether it should provide spell checking or audit trail
functionality).
The <wdk:pageTitle> tag has one attribute:
Table 4-74:)Attributes of <wdk:pageTitle> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
Title
No
ComponentName
No
No
No
No
No
No
No
User ID of the owner of the attachments. This should match the owner
of the attachments widget.
No
ComponentReference
TitleDescription
auditOn
auditHref
attachmentOn
attachmentHref
attachmentOwnerId
internationalizeOn
Child Element
Required
Value
Note: This node has no effect if the page's underlying component
is not internationalizable. The node is only used to specify that the
internationalization link should not be shown for an otherwise in~
ternationalizable icon.
No
No
No
No
No
If true, page steps must be taken in order, and page displays a Next
link to move to the next step. If false, page steps are links, and user
can jump to any step by clicking the link.
Yes
The currently active step. The contents of this node must be the ID of
one of the action subelements.
No
Specifies the resource keys for create and edit detail pages. This can
have the following three subelements:
notesOn
reportOn
spellingCheckOn
legendVisible
forced
selected
titleKeys
If the resource bundle for either the create or edit page is not specified
(or there is no titleKeys node), the page's default resource bundle
is used.
Example
<wdk:pagetitle name="pageTitle1">
<title>Test Page</title>
<legendVisible>true</legendVisible>
<attachmentOn>true</attachmentOn>
<attachmentOwnerId>gdefn000000000001011</attachmentOwnerId>
<notesOn>true</notesOn>
<reportOn>true</reportOn>
<auditOn>true</auditOn>
<spellingCheckOn>true</spellingCheckOn>
<attachmentHref>link_attachment</attachmentHref>
<auditHref>link_audit</auditHref>
<notesHref>link_notes</notesHref>
<reportHref>link_report</reportHref>
<componentName>testcomponent</componentName>
<componentInstance>insta00000022222</componentInstance>
<titleKeys>
<create>kI18n80017ComponentCreateTitle</create>
<edit>kI18n80017ComponentEditorTitle</edit>
<bundle>analytics</bundle>
</titleKeys>
</wdk:pagetitle>
<wdk:pageWidget>
This widget enables you to include one WDK page in another. The included page receives all the same request parameters
as the outer page, as well as all the other HTTP parameters. In addition, you can explicitly pass special parameters to
it.
The <wdk:pageWidget> tag has one attribute:
Table 4-76:)Attributes of <wdk:pageWidget> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
No
An HTTP parameter to pass to the called page. The called page receives
the loading page's request object and parameters. In addition, you can
explicitly pass parameters with <field> tags. Each <field> has two
children:
href
field
Only the child's model page is executed (not the view or control pages). The included page's contents are inserted into
the including page in the following way:
The contents of the included page's <wdk:head> are copied into the including page, including <wdk:label>
nodes, and so on.
The contents of the included page's <wdk:model> element are copied into a <wdk:includedPage
name="widget_name"> element, which is placed inside the including page's <wdk:model> element.
The contents of the included page's <wdk:widgets> element are copied into the including page's <wdk:widgets>
element.
Including the page in this way, instead of executing a separate view transformation on the included page, significantly
improves performance. However, the consequence of this approach is that the pageWidget cannot be attached (that is,
you must not use <wdktags:attachTo> in it, and you cannot include a pageWidget inside a <wdk:table>,
<wdk:finderResult>, and so on., widgets).
<wdk:pageWidget name="userInfoPage">
<field>
<name>user</name>
<value>dvader</value>
</field>
<field>
<name>fullInfo</name>
<value>true</value>
</field>
</wdk:pageWidget>
Full Example
The includingPage.xml looks like the following (the pageWidget node is in italics):
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
...
<wdk:head>
<wdk:title>Initiative Detail</wdk:title>
<wdk:labels>
<wdk:label name="createFormPageHeading">
<wdktags:i18n.label name="kI18n0018createFormPageHeading"/>
</wdk:label>
...
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
<wdktags:execute commandObj="anObj"/>
...
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:input name="initiativeStatus">
<wdktags:attachTo path="/InitiativeDetail/status"/>
<id><xsp:expr>detail.getStatus().getName()</xsp:expr></id>
<size>20</size>
<value><xsp:expr>enumUtil.getI18nName( detail.getStatus()
)</xsp:expr></value>
<viewOnly>true</viewOnly>
</wdk:input>
<wdk:pageWidget name="sections">
<field>
<name>viewOnlyMode</name>
<href>sections.xml</href>
<value> <xsp:expr>viewOnlyMode</xsp:expr></value>
</field>
<field>
<name>name1</name>
<value> <xsp:expr>name1</xsp:expr></value>
</field>
<field>
<name>createMode</name>
<value>
<xsp:expr>createMode</xsp:expr></value>
</field>
<field>
<name>initiativeId</name>
<value><xsp:expr>initiativeId</xsp:expr></value>
</field>
</wdk:pageWidget> </wdk:widgets>
</wdk:page>
</xsp:page>
The included page (the model page sections.xml) looks like:
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:labels>
<wdk:label name="sectionPageText"><wdktags:i18n.label
name="kI18n0018SectionPageText"/></wdk:label>
<wdk:label name="raterPageText"><wdktags:i18n.label
name="kI18n0018RatersPageText"/></wdk:label>
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdktags:execute>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:frameworkLink name="addSectionLink">
<mainPage>initiativeDetail.xml</mainPage>
<label><wdktags:i18n.label name="kI18nXXXXXAdd"/></label>
<prompt><wdktags:i18n.label name="kI18nXXXXXAdd"/></prompt>
<popup>
<input>
<name>sessionKey</name>
<value><xsp:expr>sessionKey</xsp:expr></value>
</input>
<input>
<name>reviewFormName</name>
<value><xsp:expr>name1</xsp:expr></value>
</input>
<input>
<name>initiativeId</name>
<value><xsp:expr>initiativeId</xsp:expr></value>
</input>
<action>
<type>submit</type>
<href>sectionTypes.xml</href>
</action>
<windowFeatures>
<type>custom</type>
<featureString>width=600,height=400,resizable=yes,scrollbars=yes</featureString>
</windowFeatures>
</popup>
</wdk:frameworkLink>
</wdk:widgets>
</wdk:page>
After execution, the generated model page looks like this:
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
...
<wdk:head>
<wdk:title>Initiative Detail</wdk:title>
<wdk:labels>
</wdk:labels>
<wdk:labels> <!-- these labels are from the included page -->
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
. <wdk:includedPage name="sections.xml">
</wdk:includedPage>
</wdk:model>
</wdk:form>
<wdk:widgets>
...
</wdk:widget>
</wdk:widgets>
</wdk:page>
</xsp:page>
The view page that accompanies includingPage.xml must do the following:
1. Import the view page that goes with the included page:
<xsl:import href="sections.xsl"/>
2. Use <xsl:apply-templates select="wdk:includedPage"/> to place the contents of the includedPage.
The view sheet that goes with the included page follows a slightly different guideline from other view pages:
1. The view page does not need to import the default view sheet, since the view sheet of the including page already
imports it.
2. Instead of containing a template to match wdk:model, it must contain a template to match wdk:includedPage:
<xsl:template match="wdk:includedPage">
<wdk:parameters>
The <wdk:parameters> tag can be used to aid debugging by displaying the ServletRequest parameters on the
page.
The <wdk:parameters> tag has one attribute:
Table 4-78:)Attributes of <wdk:parameters> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Example
<wdk:presence>
Used to indicate whether a particular user is available through SabaDialog. The widget inserts JavaScript code that
graphically indicates, in real time, whether the user is available.
The <wdk:presence> tag has one attribute:
Table 4-79:)Attributes of <wdk:presence> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
wdktags:attachTo
No
userId
The internal ID of the user who is being tracked (for example, "em~
pl0000001000"). If this is supplied, <userName> must also be
supplied.
If this tag is not supplied, the current user is tracked.
No
userName
Example
<wdk:model>
<employees>
<employee>
<id>emplo0001</id>
<name>User One</name>
</employee>
<employee>
<id>emplo0002</id>
<name>User Two</name>
</employee>
</employees>
</wdk:model>
<wdk:widgets>
<wdk:presence name="presence">
<wdktags:attachTo path="employees/employee"/>
<userID><wdktags:nodeRef path="id"/></userID>
<userName><wdktags:nodeRef path="name"/></userName>
</wdk:presence>
<wdk:widgets>
<wdk:promptForSave>
This widget keeps people from leaving a WDK page without saving their work. When you create a promptForSave
widget, you specify two things:
<wdk:link> widgets on the model page that are "safe" to click on (for example, because they save the contents
of the input fields), and
Widgets on the model page that the save widget should monitor.
If the user clicks on any <wdk:link> widget except the ones designated as "safe", the page checks all the monitored
widgets. If the user has changed any of them from their initial values (for example, selected a different item in a list,
typed text into a field, and so on.), the page pops up a dialog box asking the user if he or she intends to discard changes.
If the user selects "OK", the link functions normally. If the user selects "Cancel", the link has no effect, and the user
stays on the page he or she was on.
If a model page includes a <wdk:promptForSave> widget, it functions automatically on every application page
that uses the model. The view page does not need to include the widget explicitly.
The <wdk:promptForSave> tag has one attribute:
Table 4-81:)Attributes of <wdk:promptForSave> Tag
Attribute Name
Required
Value
Yes
The name of the widget. This attribute is required, but is not currently used
elsewhere in the WDK (since the View page does not place this widget expli~
citly).
name
Required
Value
No
The name of a single <wdk:link> widget. If the user clicks this widget,
the promptForSave does not interfere. The link functions normally.
safeAction
No
check
Child Element
Required
Value
Note: While this element is not required, the promptForSave
widget has no effect if there is not at least one <check> tag.
No
prompt
Message to display in the dialog box asking if the user intends to discard
changes. The dialog box contains "OK" and "Cancel" buttons. Clicking
"OK" causes the link to function normally, discarding changes. Clicking
"Cancel" leaves the user on the same page, without discarding changes.
You should phrase the prompt appropriately. (That is, "Changes will be
lost! Is that okay?" would be appropriate, but "Do you want to save
changes?" would be wrong, since clicking "OK" discards changes.)
If this tag is not present, a default prompt is used.
As an example, suppose that a particular model page had several links, two of which had the names "save" and
"cancel", and had input widgets named "user_name" and "start_date". You might define a promptForSave
widget like this:
<wdk:promptForSave name="pfs">
<safeAction>save</safeAction>
<check>start_date</check>
</wdk:promptForSave>
If the user clicks any link besides the ones named "save" and "cancel", the page examines the "user_name" and
"start_date" widgets. If the user had changed either of them from its initial value, the page displays a dialog button
saying, "Do you want to leave without saving your changes?" If the user clicks "OK", the link functions normally
(presumably losing the user's changes). If the user clicks "Cancel", the link has no effect, and the user stays on the first
page.
<wdk:sabaPicker>
The <wdk:sabaPicker> can be used to place a picker (a combination of a text field, a link, and a popup window
displaying a finder) on a page. That is, the page containing a sabaPicker displays a text field and a link. When the
user activates the link, the popup window containing the finder is launched. The following screen shows how the widget
is displayed, and the popup window it creates when clicked.
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
callbackFn
No
If true, users can type directly in the text box instead of launching
the popup window. Defaults to false.
No
No
No
No
No
Yes
enable
finderId
finderObject
id
input
label
pageHeader
No
pickerSearchAttribute
No
prompt
Child Element
Required
Value
No
No
No
No
No
No
No
No
No
required
size
textValue
useLabelTerminator
value
viewOnly
widthType
height
wdktags:attachTo
<wdk:saveLink>
This widget generates a "save" link. It behaves exactly like <wdk:frameworkLink> (described on ), with the following
exceptions:
<field>
<name>actionKey</name>
<value>update</value>
</field>
The <wdk:saveLink> widget has all the same attributes and subelements as <wdk:frameworkLink>.
<wdk:script>
This widget inserts a JavaScript expression or function call into the application page.
The <wdk:expression> tag has one attribute:
Table 4-85:)Attributes of <wdk:expression> Tag
Attribute Name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
type
Yes
arg
Child Element
Required
Value
Each <arg> node must have a type parameter. Currently, the only
supported type is String.
<wdk:script name="selectItemAlert">
<xsp:expr>
<wdktags:textValue modelSource="true"
path="selectItemAlert"/>
</xsp:expr>
</arg>
</wdk:script>
You could then call the function by including the widget in the view file, for example like this:
if (noSelect == "true")
<xsl:apply-templates
select="$wdkWidget[@name='selectItemAlert']"/>;
return false;
}
This is equivalent to using the following JavaScript code:
if (noSelect == "true")
return false;
}
Example: JavaScript Expression
You can also use the widget to insert a JavaScript expression. For example, you might include the following widget in
the model file:
<wdk:script name="chooseOperatorAlertMsg">
</arg>
<arg>condLabel</arg>
</wdk:script>
You could then insert the string by including the widget in the view file, for example like this:
if ( someCondition == true)
var chooseOperatorAle
rtMsg =
<xsl:apply-templates
select="$wdkWidget[@name='chooseOperatorAlertMsg']"/>;
}
This expression is evaluated as
if ( someCondition == true)
<wdk:table>
Used to display information in tabular form. The table can contain information or widgets.
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
name
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
The text to display if the table contains no elements. If this tag is not
present, an empty table displays the text defined by the common_la~
bels resource bundle's kI18n5006EmptyTableMessage tag.
No
emptyTableText
showEmptyTableText
Child Element
Required
Value
set it to false if, for example, the table is used to display search results
and the search has not yet been executed.
No
If true, provide an Export link to let users export table to CSV format.
If false, do not provide this link. Defaults to true.
No
Defines the heading row. The format of this tag is described in "<head>
Child Element".
No
If present, creates an Add button letting users add objects of the type
shown by the finder, and specifies what page should be launched by the
Add button. For details about the onAdd widget's contents, see below.
No
If true (the default), all rows are displayed on one page. If false,
the table is split into multiple pages, with each one having the number
of rows specified by the <pageSize> attribute.
No
No
If true, provide an Print link to let users open the table in a new win~
dow, formatted for printing. If false, do not provide this link. Defaults
to true.
Yes
Defines the rows of the table. The row is attached to a particular node
path in the model. It is repeated once for each matching node. The format
of this tag is described in "<row> Child Element".
No
export
head
onAdd
disablePaging
pageSize
row
wdktags:attachTo
No
width
The width of the table. May either be a number, specifying the width in
pixels (for example, "600"), or a percentage, specifying the width as
Child Element
Required
Value
a percentage of the browser's width (for example, "75%"). If not spe~
cified, the browser sizes the table automatically.
No
Allows you to customize the style of the table header. (This is particularly
useful for portlets.) The format of this tag is described in "<headerStyle>
Child Elements".
No
This node contains any special links you want to add to the top of the
widget. The <actionlinks> node contains one or more <data>
node, each of which specifies a link. For more information about
<data> nodes, see "<data>".
No
If set to true, the tree renders the top header in such a way as to align
to a tab. This means that the display area's corners is not rounded.
headerStyle
actionLinks
tabView
No
No
Default value is alse. If set to true, a multi-page table does not display
page numbers. Instead, each page displays a Previous and/or Next link
as appropriate.
No
No
Specifies the page to load when the Modify Table popup window is
saved and closed, or when the user clicks a Previous or Next link, a
page number, or a sortable column header. Must have one and only one
of the following child elements:
title
disablePageNumber
modifyTable
onAction
No
resetPosition
Required
Value
Yes
The heading for the column. The column headings are displayed in the
order they are defined. Column headings may contain text or widgets.
column
The <column> elements define the headers for each column. These elements have two optional attributes:
Table 4-90:)Attributes of <wdk:table><head><column> Tag
Child Element
Required
Value
No
No
width
type
If the column is of type checkbox, it can have the following child elements:
Required
Value
No
If true, display a Check All box in the column header. Users can click
that box to check or uncheck all columns. Default is false.
Yes
Yes
No
checkAll
id
value
label
Required
Value
Yes
An XPath expression, relative to the node the table is attached to, specifying
the root node for rows. There is one row in the table for each corresponding
node.
path
column
Required
Value
Yes
Contents of the column. This may contain text or widgets. Any <wdk~
tags:nodeRef><wdktags:nodeRef> tags are specified relative to the node
that this row is attached to (not the node that the table, as a whole, is
attached to). Of course, the <wdktags:nodeRef> can override this
path by defining a source attribute.
Required
Value
No
If specified (and no label tag is specified) then the display name of the
component is used to autogenerate the label of the link.
No
Indicates what type of action is used to create the object. This is used
in combination with component to generate the label for the button,
if ?the onAdd does not have label or prompt tags. Appropriate
values are create (default) or add. If a label isn't specified, the ap~
plication generates an appropriate label from this and the component's
display name (for example, "Add Course", "Create Goal").
component
actionType
Required
Value
No
No
Style for tree columns that contain content (that is, not the first
and last columns, which provide the rounded border for the table
containing the tree).
No
No
No
No
endStyle
contentStyle
textStyle
sortLinkStyle
sortEndStyle
sortContentStyle
Example
Suppose a particular model page contained a section describing students, along these lines:
<wdk:model>
<students>
<student>
<name>Neville Longbottom</name>
<id>nlongbot</id>
<house>Gryffendor</house>
</student>
<student>
<name>Cho Chang</name>
<id>cchang</id>
<house>Ravenclaw</house>
</student>
</students>
</wdk:model>
<wdk:table name="Students">
<wdktags:attachTo p
ath="studen t
node... -->
<head>
<column>Name</column>
<column>House</column>
</head>
to a <student> subnode.-->
<wdk:link name="editLink">
<href>EditStudent.xml</href>
<label>
<wdktags:nodeRef path="name"/>
particular student-->
</label>
<field>
<name>id</name>
<value>
<wdktags:nodeRef path="id"/>
</value>
</field>
</wdk:link>
</column>
<column>
<wdktags:nodeRef path="house"/>
</column>
</row>
</wdk:table>
The table ends up looking something like this:
Name
House
Neville Longbottom
Gryffendor
Cho Chang
Ravenclaw
Draco Malfoy
Slitherin
Parvati Patil
Gryffendor
The exact format of the table is determined by the site's stylesheets. Each user's name is a hyperlink. If the user clicked
on a user's name, the EditStudent page is launched and is passed an id HTML parameter, whose value would be
that particular student's user ID.
Note that the column headings could also be widgets. For example, they might be set up so the user could sort the table
by clicking on a column's heading.
<wdk:textarea>
The <wdk:textarea> widget creates a field for multi-line text entry. (The standard widget library transforms it into
an HTML <textarea> tag.)
The <wdk:textArea> tag has one attribute:
Table 4-96:)Attributes of <wdk:textarea> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated element. If not specified, the value
of the widget's name attribute is used.
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
No
No
No
id
wdktags:attachTo
label
required
width
height
value
viewOnly
useLabelTerminator
<wdk:textarea name="abstract">
<width>80</width>
<height>10</height>
<required>true</required>
<label>Abstract</label>
</wdk:textarea>
<wdk:timeInput>
The wdk:timeInput widget is used for inputting a time (hour and minute). The time value should be in the time format
returned by the com.saba.util.DateConvertUtil.getTimeFormatter(ServiceLocator) method.
The <wdk:timeInput> tag has one attribute:
Table 4-98:)Attributes of <wdk:timeInput> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
No
This id is the name of the generated element. If not specified, the value
of the widget's name attribute is used.
No
No
No
No
If true, the widget is displayed in a way indicating that its value is re~
quired (for example, the label might be displayed in red). Defaults to
false.
No
No
id
wdktags:attachTo
label
value
required
viewOnly
useLabelTerminator
<wdk:timeInput name="startTime">
<wdktags:attachTo path=""/>
<label>Start Time</label>
</wdk:timeInput>
<wdk:tree>
This widget displays hierarchical data in tree form. It has one attribute:
Table 4-100:)Attributes of <wdk:tree> Tag
Attribute Name
name
Required
Value
Yes
The name of the widget. The view stylesheet uses this name to identify the
widget. For example,
<xsl:apply-templates select="wdk:widget[@name='your~
WidgetName']"/>
Required
Value
Yes
No
Used for the title of the tree. The title appears in the upper left
hand corner of the widget in the standard typeface for table and
finder titles.
No
If set to true, the tree renders the top header in such a way as to
align to a tab. This means that the display area's corners is not
rounded.
treeExpr
treeTitle
tabView
No
This node contains any special links you want to add to the top of
the widget. The <actionlinks> node contains one or more
<data> node, each of which specifies a link. For more informa~
tion about <data> nodes, see "<data>".
No
Allows you to specify custom style features for the tree's header.
This node is described fully in "<headerStyle> Child Elements".
No
Text to show if the tree is empty (that is, does not have a root node
or any children).
No
Yes
Specifies the way the tree should be arranged. There are two sup~
ported values:
actionLinks
headerStyle
emptyText
mainColumnHeader
layoutFlavor
No
mainPage
Child Element
Required
Value
Note: Either <mainPage> or <href> must be present.
No
href
No
If true, the widget initially displays with the root node open and
the root's child elements visible. If false, the widget initially
displays with the root node closed and no other nodes visible.
Defaults to false.
No
If false, do not display the root node of the tree. Default value
is true.
No
If true, the widget initially displays with all nodes open (overrid~
ing any setting for <rootNodeOpen>). If false, the widget
initially displays either the root node or the root node and its im~
mediate children, depending on the setting of <rootNodeOpen>.
Defaults to false.
No
No
Image to use as the connector to the last node in the tree. If not
present, default graphic is used.
Yes
Each tree may have one or more node types. Each node in the tree
has one of these types. The node type specifies how the node is
displayed.
rootNodeOpen
displayRootNode
allNodesOpen
middleNodeConnectorGIF
lastNodeConnectorGIF
nodeType
Child Element
Required
Value
This element is described further in "<nodeType> Child Elements".
Yes
Specifies what happens when the user opens or closes a node. This
must contain a single <data> element, specifying the link to
open. The <data> element is described further in "<data>".
No
Specifies the maximum number of nodes the tree can display. The
default value is 100. If there are more nodes than this, the tree
displays only maxNodes.
No
Text to display if the tree was truncated because it had more data
than the maxNodes limit.
No
No
No
No
Specifies whether the tree should show a Modify Table link, al~
lowing the user to modify the tree's data. Default is true.
No
No
Specifies the page to load when the Modify Table popup window
is saved and closed.
No
openCloseWidget
maxNodes
nodeLimitReachedText
enableWidgetSpacing
leftWidgets
rightWidgets
modifyTable
backGroundStyle
onAction
head
Required
Value
No
No
No
href
mainPage
column
Required
Value
No
No
Style for tree columns that contain content (that is, not the first
and last columns, which provide the rounded border for the table
containing the tree).
No
No
No
No
endStyle
contentStyle
textStyle
sortLinkStyle
sortEndStyle
sortContentStyle
nodeTypeName
Required
Value
Yes
Unique name for this node type. The ITreeModel interface has
a method getNodeType, which returns the type of a node. The
Child Element
Required
Value
return value must match the <nodeTypeName> for one of the
tree's <nodeType> elements.
Yes
No
No
No
data
closedNodeGIF
openNodeGIF
backGroundStyle
<wdk:tree name="wdkTreeWidget">
<treeExpr>tree</treeExpr>
<mainPage>viewTree.xml</mainPage>
<displayRootNode>true</displayRootNode>
<middleNodeConnectorGIF>
<xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_expand.gif
</middleNodeConnectorGIF>
<lastNodeConnectorGIF>
<xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_expand_last.gif
</lastNodeConnectorGIF>
<nodeType>
<nodeTypeName>personNode</nodeTypeName>
<openNodeGIF>
<xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_minus.gif
</openNodeGIF>
<closedNodeGIF>
<xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_plus.gif
</closedNodeGIF>
<data>
<wdk:frameworkLink name="abc">
<label><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></label>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<href>viewTree.xml</href>
<mainPage><xsp:expr>onLeafMainPage</xsp:expr></mainPage>
<field>
<name><xsp:expr>OrgTree.kLeafHandler</xsp:expr></name>
<value><wdk:nodeProperty><name>id</name></wdk:nodeProperty></value>
</field>
</wdk:frameworkLink>
</data>
</nodeType>
</wdk:tree>
locator )
throws SabaException {
null, locator );
mServiceLocator = locator;
/**
*/
AudienceTypeModelNode.kNodeType );
return type;
/**
In the example,
getChildren( "A" )
*/
throws SabaException {
return
((AudienceTypeModelNode)node).getChildren();
/**
* before calling
*/
SabaException {
/**
*/
SabaException {
return null;
/**
*/
return true;
/**
*/
throws SabaException {
return
((AudienceTypeModelNode)node).getParent();
/**
*/
return mRootNode;
}
It uses a single ITreeModelNode class with the following definition. (Note that while this example uses a single
class for all nodes, you are free to use multiple node classes, using a different class for each node type, if you wish.)
AudienceTypeTreeModel tree =
(AudienceTypeTreeModel)s.getAttribute("audTree");
if ( tree == null )
<wdk:tree name="wdkTreeWidget">
<treeExpr>tree</treeExpr>
oneLevelTree-->
<mainPage>browseEditAudType.xml</mainPage>
<displayRootNode>true</displayRootNode>
<!--
<middleNodeConnectorGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_expand.gif</mi
ddleNodeConnectorGIF>
<lastNodeConnectorGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_expand_last.gif</la
stNodeConnectorGIF>
-->
<leftWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconEdit</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>/party/createNewAudienceType.saba</mainPage>
<input>
<name><xsp:expr>AudienceTypeBean.kAudienceTypeId</xsp:expr></name>
<value><wdk:nodeProperty><name>id</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeName</name>
<value><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeDescription</name>
<value></value>
</input>
</wdk:frameworkLink>
</data>
</leftWidgets>
<rightWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconDelete</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>browseEditAudType.xml</mainPage>
</wdk:frameworkLink>
</data>
</rightWidgets>
<nodeType>
<nodeTypeName>audiencetype</nodeTypeName>
<openNodeGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_minus.gif</openNodeGIF>
<closedNodeGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_plus.gif</closedNodeGIF
>
<leftWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconEdit</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>/party/createNewAudienceType.saba</mainPage>
<input>
<name><xsp:expr>AudienceTypeBean.kAudienceTypeId</xsp:expr></name>
<value><wdk:nodeProperty><name>id</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeName</name>
<value><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeDescription</name>
<value></value>
</input>
</wdk:frameworkLink>
</data>
</leftWidgets>
<!--
<rightWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconDelete</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>browseEditAudType.xml</mainPage>
</wdk:frameworkLink>
</data>
</rightWidgets>
-->
<data>
<wdk:frameworkLink name="abc">
<label><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></label>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<!--<style>sbSideTeamTextUnselected</style>-->
<mainPage>browseEditAudType.xml</mainPage>
</wdk:frameworkLink>
</data>
</nodeType>
<nodeType>
<nodeTypeName>seatcat</nodeTypeName>
<openNodeGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_minus.gif</openNodeGIF>
<closedNodeGIF><xsp:expr>wdkSite.getImageRoot()</xsp:expr>/i_plus.gif</closedNodeGIF
>
<leftWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconEdit</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>/party/createNewAudienceType.saba</mainPage>
<input>
<name><xsp:expr>AudienceTypeBean.kAudienceTypeId</xsp:expr></name>
<value><wdk:nodeProperty><name>id</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeName</name>
<value><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></value>
</input>
<input>
<name>audienceTypeDescription</name>
<value></value>
</input>
</wdk:frameworkLink>
</data>
</leftWidgets>
<rightWidgets>
<data>
<wdk:frameworkLink name="abc">
<icon><xsp:expr>wdkSite.getImageRoot() + "/" +
ImageConstants.kImgIconDelete</xsp:expr></icon>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<mainPage>browseEditAudType.xml</mainPage>
</wdk:frameworkLink>
</data>
</rightWidgets>
<data>
<wdk:frameworkLink name="abcd">
<label><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></label>
<prompt><wdk:nodeProperty><name>displayName</name></wdk:nodeProperty></prompt>
<!--<style>sbSideTeamTextSelected</style>-->
<style>sbListOddText1</style>
<mainPage>browseEditAudType.xml</mainPage>
</wdk:frameworkLink>
</data>
</nodeType>
</wdk:tree>
<wdk:validatorWidget>
Checks if the user has filled in all required information on the page's forms before submitting the information. When
you define the widget, you specify which <wdk:link> widgets it should modify. If the user clicks on any of those
links, the page verifies that the user has properly filled/selected all widgets that contain <required> child nodes. If
the user has not filled in any required widget (or selected an item from a required list, and so on.), or if a widget has
been filled in improperly (for example, text length exceeds the permitted length, or a numeric value is too low or too
high), the link does not function. Instead, the user is shown a dialog box describing the problem.
The validator judges a widget to be "required" if it contains a <required> child tag, or if the WDK engine can
determine (by examining the component dictionary) that the widget corresponds to a field that is required by the underlying
Saba object. In addition, if the component dictionary contains information about a widget's appropriate values (for
example, length of string, data type), the validator checks to see that the widget complies. For more information about
the component dictionary, see "Component Dictionary Support".
Unlike most widgets, the validator widget does not need to be included by the view page. Instead, the validator widget
modifies the behavior of links defined on the model page. If any of those links is used by a view page, the validator
widget functions-if the user clicks on the link, the page verifies that the forms are properly filled in before the link
functions.
The <wdk:validatorWidget> tag has one attribute:
Table 4-105:)Attributes of <wdk:validatorWidget> Tag
Attribute Name
Required
Value
No
The name of the widget. Since the widget is not placed on the page, naming it
is optional.
name
saveLink
Required
Value
Yes
Child Element
Required
Value
If you want the widget to monitor more than one link, put in multiple
<saveLink> tags.
No
The message to display if the user tries to submit the form without as~
signing values to all required fields. If <prompt> is not supplied, the
text from the common_labels resource's kI18n5006Required~
Prompt property is used.
No
The message to display if the text in a required field is longer than the
widget permits. If <maxLengthPrompt> is not supplied, the text
from the common_labels resource's kI18n5006TooLongPrompt
property is used.
No
No
No
The message to display if a widget value is not of the right data type
(for example, if a user enters alphabetic characters in a numeric field).
If <datatypePrompt> is not supplied, the text from the common_la~
bels resource's kI18n5006WrongDatatypePrompt property is
used.
prompt
maxLengthPrompt
minValuePrompt
maxValuePrompt
datatypePrompt
In order to take advantage of the component dictionary support in WDK, developers must do the following:
1. When generating the XML for a component, developers need to create the XMLFacet for the component.
2. Generate the XML for a component by using the visit(TagData ...) methods of the visitor.
3. Attach the widget to the XML representation of the component attribute.
Example
This example illustrates the various steps in adding component dictionary support to a component, and to the WDK
pages that use it. Some of these steps are followed by the developers who create Saba components (most of which are
shipped as part of the API). Others are followed by application page developer.
The example takes the case of a WDK page that is displaying information from a DomainDetail object. This object
is a Saba component that is integrated with the Component Dictionary.
/**
*/
//[1]
//[2]
visitor.visit(facet.getTagData(kDescriptionAttribute),
mDescription);
//[4]
<name cdID="Domain:name">Domain1</name>
<description
cdID="Domain:description">Domain1 description</description>
The cdID attribute encodes minimal metadata about the component attribute represented by this node.
pServletRequest req,
IXMLVisitor visitor,
nId");
}
The Model Page
The model page has to generate a widget that takes advantage of the component.
The <wdk:model> section contains code that generates the XML version of the component:
<wdktags:execute
manager="com.saba.client.domain.DomainCommandManager"
command="getDomainAndParents" return="model">
mode="in"/>
</wdktags:execute>
The <wdk:widgets> section contains input widgets that map to the DomainDetail fields:
<wdk:input name="Name">
<id>name</id>
<value><wdktags:nodeRef path="."/></value>
</wdk:input>
<wdk:input name="Description">
<id>description</id>
<value><wdktags:nodeRef path="."/></value>
</wdk:input>
Note that each widget is attached to the attribute it is supposed to display and that the widgets do not contain <required>
tags. Since the component dictionary specifies that these fields are required, it is not necessary to explicitly include
"<required>true</required>" nodes. In this example the <label> tag is not defined either, so any default
label defined for that field in the component dictionary would be used.)
One can override the component dictionary information by providing the appropriate field directly. For example, the
code below would make description required even if the component dictionary says otherwise:
<wdk:input name="Description">
<id>description</id>
<value><wdktags:nodeRef path="."/></value>
<required>true</required>
</wdk:input>
Chapter
5
Model Objects
Topics:
Overview
Model Object Lifecycle
Defining and Using Model Objects
Initializing and Saving/Restoring
State
Managing Widgets
Special WDK Tags
getModelObject()
<wdktags:execute>
Advanced Model Object
Techniques
This chapter describes how to write and use model objects with WDK application
pages.
Overview
Saba constructs a WDK page by combining three separate XML pages:
The model file contains the page's data. It describes all the widgets on the page and can also include Java code to
retrieve or construct information.
The view file describes how the information on the page is displayed.
The control file specifies what model and view files are being used. Thus, you can specify a particular application
page by specifying the control file.
You can also associate a model object with an application page. The model object is declared in the control file, and is
used whenever that application page is generated. The model object performs several functions:
The model object can be used to preserve information when a page is reloaded-for example, when a user clicks on
a column header to sort a page's data.
The Saba platform queries the model object to find out whether each widget is visible, and if it is visible, whether
it is editable.
The model object can also function as a command object (if it implements the ICommand interface).
All page parameters are automatically passed to the model object when it is initialized. This can simplify the model
file's code, because you need only to declare a parameter in the model file (with
<wdktags:param><wdktags:param>) if it is used in the model file. If the parameter is only used in the model
object's code, you do not need to declare it in the model file.
Similarly, the model object can pass parameters to the next page, making it unnecessary to declare them as outbound
<wdktags:param> parameters in the model file.
The model object can be used to share information between various pages. For example, if a page redirects to another
page, it can arrange for its model object to persist so the replacement page can call its methods.
You can add any methods you like to the model object, and the model page can invoke the methods whenever it
likes. This can make the model page much cleaner. For example, to make an elaborate calculation, put all the code
into a single model object method and then invoke the method from the model page, rather than putting all the code
in the model page directly.
Note that an application page does not need a model object. It is entirely up to you whether to create a model object for
any particular application page. However, having a model object simplifies many tasks, and there are some things you
can only do with a model object (for example, dynamically deciding whether a particular widget should be displayed).
5. Saba invokes the object's decodeState method. This lets the object initialize certain of its values. The object is
passed a stateDecoder object, which contains any public parameters passed to the application page. It may also
contain other information, as noted in Step 9 below.
6. Saba invokes the object's performAction method. This method can take any actions you like. An application
page can usually put most of its Java calculations in this method.
7. If the model page contains Java code which calls on the model object, those calls are processed. In particular, the
model object can implement the ICommand interface. In this case, the model page can invoke the model object to
make it generate an XML node.
8. Saba then queries the model object about every widget specified in the model page. Saba asks the model object
whether each widget is visible. If the widget is visible, Saba then asks whether it is view only, and queries whether
the widget should be initialized from the request object.
Note: Some widgets such as tables can contain other widgets. If a container widget is not visible, none of its
contents are visible, and the model object is not queried about those other widgets.
9. When the user leaves the page, Saba calls the model object's encodeState object. This lets the model object
record its current state information to be passed along to the next object. The object can pass both "public" and
"protected" state parameters. Public parameters are passed to the next page like any other HTTP parameter. Protected
parameters are only passed to the next page if it has the same control file as the current page (that is, if the application
page is reloaded), and in certain other situations described in "Advanced Model Object Techniques".
When the HTTP request object is destroyed, so is the model object. This usually happens when the user leaves the page,
although in certain circumstances, the request object may persist until the next page (for example, if the first page uses
a <wdktags:redirect> tag). As discussed in "Advanced Model Object Techniques", in those circumstances you
can have the second page make Java calls to the first page's model object. In most cases, however, the model object is
called only while its own page is active.
decodeState: Used to initialize the object with any state information, as described in "Initializing and
Saving/Restoring State"
encodeState: Enables the model object to save its state, as described in "Initializing and Saving/Restoring State".
performAction: Called after the object is created and initialized. The object can take any actions here that it
needs to, to prepare itself to handle the other methods.
isVisible, isViewOnly, initializeFromRequest: Called for each widget, to determine whether and
how the widget should be displayed. These are discussed further in "Managing Widgets".
The model object must also provide a constructor that takes a single argument, an HttpServletRequest object.
You can write any other methods and implement other interfaces as needed. For example, many model objects implement
the ICommand interface. This enables them to function as command objects and generate XML for display on the Saba
application page. You can also write methods to perform any calculations you need on the model page.
For details on the various classes and interfaces, see the Java API Reference Guide.
<?cocoon-process type="wdk"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="1234567">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version>
<wdk:model rdf:resource="myCustomModel.xml"/>
<wdk:view rdf:resource="myCustomView.xsl"/>
<wdk:modelObject>com.mycorp.model.MyCustomModel
</wdk:modelObject >
<wdk:widgets
rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
<wdk:uiframework/>
</rdf:Description>
</rdf:RDF>
Saba automatically creates the model object when the page is created, as described in "Model Object Lifecycle".
To get the model object from Java code within the model page, call the method getModelObject (which returns an
IModelObject). For example, if your model object belonged to the class MyCustomModel and had a method named
doCalculations, you could invoke it from the model file by including code like this:
<xsp:logic>
model.doCalculations();
</xsp:logic>
In addition, there are several special-purpose WDK tags to cause the model object to perform specific actions. These
tags are described below.
Decoding State
After a model object is created, Saba calls its decodeState method. This method is passed a StateDecoder object,
which contains information about the parameters passed to the page. There are two kinds of parameters encoded in this
object:
public parameters include all HTTP parameters passed to the application page, whether they are specified by the
previous model object, by a <wdktags:param> tag, or some other way. They can be examined by calling the
StateDecoder.getPublicParameter methods.
protected parameters include any protected parameters passed by the previous page's model object. These parameters
are only passed if the previous application page used the same control file, or in the circumstances described in
"Advanced Model Object Techniques". They can be examined by calling the
StateDecoder.getProtectedParameter methods.
The model object's constructor is passed the HttpServletRequest object. Because this object contains all the
parameters passed to the application page, you can retrieve all those parameters in the constructor instead of waiting
until decodeState is called.
Encoding State
When a user leaves an application page, Saba calls the model object's encodeState method immediately before
destroying the object. The method is passed a StateEncoder object. The model object can store any parameters into
this object. The parameters stored can be either public or protected:
public parameters are passed to the next page as HTTP parameters. Thus, they can be used to pass information to
the succeeding application page, or even to a non-Saba web page. In addition, if the succeeding page has a model
object, the parameters are passed to that object's decodeState method.
protected parameters are passed to the succeeding page only if it uses the same control file as the current page (that
is, if the page is reloading itself). Otherwise, they are discarded.
Note: There are other circumstances where protected parameters are made available to a succeeding page, even
if it does not use the same control file. These circumstances are described in "Advanced Model Object
Techniques".
Managing Widgets
You can use a model object to manage the widgets on a page. If an application page has a model object, Saba queries
that object to find out if each widget should be displayed, and if so, whether the widget should be editable, and whether
it should be initialized from the request object. This happens after Saba creates the model object and invokes its
decodeState and performAction methods.
Saba identifies each widget with a WidgetInfo object. This object contains the widget's name (specified by the
widget's <name> tag) and its form ID (which is usually the value of the <id> tag, or of the <name> tag if there is no
<id>).
For each widget, Saba does the following:
1. Saba calls the model object's isVisible() method, passing in a WidgetInfo object which identifies the widget.
If the method returns false, the widget is not displayed, and none of the other methods are called. If the method
returns true, the widget is displayed normally.
Note: If the widget contains other widgets, and it is not visible, none of the contained widgets are visible. For
example, if the model object says that a particular table widget is not visible, the table is not displayed, which
means none of the widgets in the table are displayed. Neither initializeFromRequest() nor
isViewOnly() is called for any of those widgets.
2. If the widget is visible, Saba checks to see if the widget's definition in the model page contains an
<initializeFromRequest> tag. If the tag is present, Saba obeys it. If the tag is not present, Saba calls the
model object's initializeFromRequest() to find out whether the widget should be initialized from the request
object. (For more information about initializing widgets from the request object, see
"<initializeFromRequest>""<initializeFromRequest>" on page 4-8.)
3. If the widget is visible, Saba checks to see if the widget's definition in the model page contains a <viewOnly> tag.
If the tag is present, Saba obeys it. If the tag is not present, Saba calls the model object's isViewOnly() to find
out whether the widget should be displayed in a view-only state.
Note that the initializeFromRequest() and isViewOnly() methods are called only if the model page does
not specify, through the appropriate XML tags, whether the widget should be initialized from request and/or is view
only.
getModelObject()
You can use this method from any Java code in a model page (for example, within an <xsp:logic> tag). It returns
the model object for the page. For example, if your model object has a method named doCalculations, you could
invoke it with this code:
<xsp:logic>
thisPageMO.doCalculations();
</xsp:logic>
<wdktags:execute>
The <wdktags:execute> tag provides a useModelObject attribute. If this attribute is present and set to "true",
the model object is invoked as a command object, generating XML. (The model object must implement the ICommand
interface.) Using the attribute is the equivalent of using code like
<xsp:logic>
</xsp:logic>
<wdktags:execute commandObj="thisPageMO">
For more information about executing commands through command objects, see the section on
"<wdktags:execute>""<wdktags:execute>" on page 3-24.
<xsp:logic>
</xsp:logic>
As soon as you have the earlier page's model object, you can access any of its public methods and fields, as with any
other object. Depending on what class is making the calls, you can also access the model object's package-level, protected,
or private methods and fields. If you are calling from Java code embedded in the model file, that code is compiled as a
class that is in its own package and object hierarchy and cannot call any protected or private methods.
Public parameters are available to the successor page, no matter what it is. They are passed as HTTP parameters,
and if the successor page has a model object, they are passed to its decodeState method.
Protected parameters are only made available to the successor page if the two pages use the same control file. That
is, if the page reloads itself. Otherwise they are inaccessible to the successor.
There are times when you might want a page to be able to pass protected parameters to a small number of other pages.
For example, you might design a "wizard", where the user goes through a sequence of pages to perform an action. In
such a case, you might want each page to pass its full state information to any other page in the wizard, so the user could
jump from step 3 back to step 1 and see all the information he entered on that page.
You can do this by making the model object part of a model group. You do this by adding a group attribute to the
model object's declaration in the control file:
</wdk:modelObject>
If a model object belongs to a group, its decodeState method can read protected parameters which were passed by
any model object in that group.
For example, suppose a multi-page wizard has a model object for each page, named WizModel1, WizModel2, etc,
with all the model objects belonging to the "WizModelGroup" group. If a user is using the first page of the model,
and the user leaves the page, the model object stores its state in the StateEncoder, as public and protected parameters.
When the successor page launches, its model object's decodeState method is called. If the successor page is not a
page in the wizard (because the user went to another part of the application), Saba sees that the successor page's model
object does not belong to the same model group. That page receives the public parameters, not the protected ones.
However, if the successor page is another page in the wizard (because the user clicked the Next button), Saba sees that
the new page's model object belongs to "WizModelGroup", like the preceding page. In that case, the successor page's
decodeState is able to examine both the public and the protected parameters.
Note: You should note the difference between assigning a model object a key (as discussed in "Calling the Model
Object From a Second Page") and making it part of a group. If a model object is given a key, that lets successive
pages interact with the model object itself, calling its methods and examining its fields. If a model object is part of
a group, the successive pages do not interact with the model object directly-the model object is destroyed normally
when they user leaves the page. Instead, the successor pages (if their model objects belong to the same group) can
examine the protected state information which the first object stored before it was destroyed.
Chapter
6
Coding Customization (Java)
Topics:
Types of Objects
Typical Tasks
Executing Java Code
This section describes how to write Java code for use with WDK pages. This
code can retrieve information from the Saba data store and also add new
information.
Types of Objects
The Saba API defines classes designed to manage a wide range of different kinds of data. For example, objects based
on the RoleManager and RoleDetail classes retrieve and modify information about Saba roles. In addition, the Saba API
defines several infrastructure classes that you use with all kinds of data.
Infrastructure Classes
These objects provide core functionality for using the API.
ServiceLocator
This is a general utility class. It provides several methods for creating and managing delegate objects. Many of the
methods are intended for Saba internal use only. However, three methods are useful to API programmers:
getManager(): Creates the specified Manager object. This method is used to create stateless Managers (that is,
Manager objects without a corresponding ManagerHome).
getHome(): Creates the specified Home object. This might be the Home for a package which uses the Home/Entity
model (as described in "Delegate Objects"), or the ManagerHome for a Manager object which contains state
information.
getReference(): These methods construct a Reference object, based on the values you supply. (For example,
one method constructs the reference object from passed class and primary key objects, another from class and object
ID, etc.) These methods do not verify that the values passed correspond to data in the Saba data store. They construct
appropriate reference objects based on the assumption that the parameters are valid. (This makes the methods very
useful when you know the reference object is valid, because the methods save the cost of a network database query.)
When you create a ServiceLocator object, provide an authentication certificate (which you can get from
SabaLogin.authenticate, or by having it passed to your WDK page). This lets the ServiceLocator set the
appropriate permissions for delegate objects that it creates. Note, however, that the getReference method is static
and can be run without instantiating ServiceLocator-thus, you can call getReference() without having an
authentication key.
Delegates
This special-purpose class defines constants for each delegate provided by the Saba API. Each of these constants is
implemented as a static member of the Delegates class and is also identified by a static field in that class. These
constants can be passed to the various ServiceLocator create methods to identify what kind of delegate you want
to create. For example, to create a PartyManager object, call the ServiceLocator.getManager() method:
locator.getManager(Delegates.kPartyManager);
You can find the constant for a particular delegate by consulting the Delegates reference page.
SabaLogin
This object enables you to generate a ServiceLocator, which identifies which user has logged in and authenticates
that user's session.. Many Saba objects require a ServiceLocator. The object lets Saba know which user account
is performing the action, and thus verify that it has permission to do so. (For example, a Manager object might not let
a user delete data from the data store if the user does not have the right permissions.)
You do not usually need to use a SabaLogin object directly. You can always call the getServiceLocator() method
within a WDK page or a Command object to obtain the current valid ServiceLocator object. However, in some
circumstances, you might find it useful to be able to log in directly. For example, if you are testing Java code, you might
want to generate a ServiceLocator within the code, thus letting you run the code in a standalone application (instead
of launching the code from a WDK page). To generate a ServiceLocator, call one of the static
SabaLogin.authenticate methods.
IPrimaryKey
Each Reference Object (described on page ) can be identified by a Primary Key object. This object, which implements
the IPrimaryKey interface, should be treated as an opaque token. That is, you can find out the primary key of a
particular reference object by calling its getPrimaryKey() method. You can find the reference object for a particular
primary key by calling ServiceLocator.getReference(), or the appropriate delegate object's
findByPrimaryKey().
In previous Saba4 releases, reference objects were identified by Object ID strings. Some API packages still use these
strings. You can convert an Object ID string into a Primary Key by constructing an ObjectId object (which implements
IPrimaryKey). Pass the ID string to the ObjectId(String) constructor. Similarly, if you ever need to convert
a primary key into an Object ID string, you can do this by calling the primary key's toString() method. However,
whenever possible, you should use primary key objects instead of object ID strings.
Class Structure
Each API package has several related objects. The objects can be divided into two broad categories:
Delegate objects: These objects interact with the Saba data store. One example of a "delegate object" is a Manager,
which you would use to create, find, or modify information in the data store.
Helper objects: These objects contain information from the Saba data store but do not directly interact with it. They
are created by delegate objects, and passed to delegate object methods, but altering a helper object's instance data
fields does not directly change the underlying data in the Saba data store.
Helper Objects
In most packages, the helper objects come in pairs:
Reference objects, which identify some small collection of persistent data in the Saba data store, such as all the
information about a client.
Detail objects, which contain a copy of the relevant data fields. For example, a ClientDetail contains information
about a client, such as its name, time zone, address, etc. Very frequently, detail objects contain other detail objects
(for example, a ClientDetail has, as one of its fields, an AddressDetail) and/or reference objects.
Reference Objects
A reference object corresponds to some small collection of persistent data in the Saba site's data store. For example, a
particular Saba site might have a thousand employees listed. Information about each employee is kept in the data store.
The reference object uniquely identifies the set of data, but it does not itself contain the data. Instead, it provides the
information that you need to retrieve the data from the Saba data store. For example, to read or change a particular
employee's information, use a manager object to retrieve the appropriate employee reference object. When you have the
reference object, you can use other delegate object methods to retrieve or change the underlying data. The most common
way to find a reference object is by calling a method in the appropriate delegate object. However, particular objects
might provide other ways. (For example, a "course" object might provide a method to return the appropriate "instructor"
object for the course.)
Each reference object is uniquely associated with a primary key. You can get the primary key by calling the object's
getPrimaryKey() method (inherited from the IReference interface).
You should never create reference objects by calling the constructor. If you need a particular reference object, you get
it by calling some other object's method (usually a Manager method), or by constructing it with the
ServiceManager.getReference() methods.
Note: There are two different ways of getting a reference object when you know its primary key: You can call the
delegate object's findByPrimaryKey() method, or you can call ServiceManager.getReference().
There are two differences between these approaches. First, the delegate objects' findByPrimaryKey() methods
check to make sure that the reference object exists (that is, that the primary key corresponds to data in the Saba
data store). ServiceManager.getReference() constructs the reference object by using the primary key
and the specified class, without checking the data store. Thus, if you know that the primary key is valid, you should
use ServiceManager.getReference(), because the method does not incur the cost of a database check.
Second, because ServiceManager.getReference() is a static method, you can use it even if you do not
have a session key. In order to create a delegate object, you need an instantiated ServiceManager object, which
means you must have a valid session key.
Detail Objects
Some delegate objects provide methods to retrieve or change specific data fields from the Saba data store. However,
these operations are expensive, because each one requires a separate database query over the network. For this reason,
most API packages provide detail objects. A detail object contains all the data fields for the reference object. In most
cases, you get detail objects by calling a method in the appropriate delegate object. The manager retrieves all the data
fields with a single database query, constructs the detail object, and returns it.
Detail objects also implement IReference. Every detail object is also a reference object. If you need a reference
object, you can cast the appropriate detail object to IReference.
Most detail objects implement the IXMLObject interface. This means you can generate an XML representation of the
detail object by visiting it with an appropriate IXMLVisitor object.
There are times when you create a detail object directly, by calling its constructor. For example, as noted above, the
usual way to add an object into the Saba data store is to create a new detail object, fill in its fields appropriately, and
pass it to the manager's "create" method.
Delegate Objects
Delegate objects are used to interact with the Saba data store. You can use delegate objects for several purposes. For
example, you can search for a reference object meeting certain requirements ("Find all clients whose names contain
'Smith'"), find the detail object for a particular reference object, or add a new object into the data store. In all cases, you
retrieve the appropriate delegate object by calling a method in the ServiceLocator object. You do not create the
delegate directly, by calling its constructor. (In most cases, only the delegate's interface is part of the API. When you
call the ServiceLocator method, it returns an object which implements the interface.)
Different Saba packages use two basic models of delegate objects: Manager delegates, or Home/Entity delegates.
If the package follows the Home/Entity model, it contains a pair of delegate classes: a Home class (for example,
ClientHome), and an Entity class (for example, ClientEntity). The Home object provides methods for creating
a reference object (and the corresponding data in the Saba data store), finding a reference object by its primary key,
and deleting a reference object (and its corresponding data). The Entity object provides methods to retrieve or change
information about a given reference object.
If the package follows the Manager model, it provides a single Manager class (for example, CurrencyManager).
This manager provides all the delegate functionality: it provides methods to find a specific reference object, retrieve
or modify the object's data, delete the object, etc. It may also provide special-purpose methods needed by that specific
package. In some cases, the package also contains a ManagerHome class, used to create the Manager object (if the
Manager contains state information).
An API package uses the Home/Detail model if the delegate objects provide simple, straightforward functionality
(searching for an object containing specific information, modifiying data, etc.). If the package needs to provide more
elaborate delegate functionality, it uses the Manager model. In addition, some packages use the Manager model so a
single Manager can handle a wide number of helper objects. For example, the PartyManager object is used to create
and modify Client objects, Employee objects, etc.
Manager Objects
A manager object creates and retrieves a particular kind of object. For example, to find, modify, or create a Saba user,
use a PartyManager object.
The API specifies a manager interface, not the class of the manager. You retrieve a manager object by requesting it from
a ServiceLocator. For example, to create a new employee, you need a PartyManager. You get this by calling the
ServiceLocator's getManager method, passing in the appropriate constant (specified by the Delegates class).
The method returns a manager object which implements the PartyManager interface. You can then use that object
to perform the various operations.
The manager object provides a wide range of functionality. Different manager objects provide different methods, but
the typical functions include:
You do not create manager objects directly. There are two different ways to create a Manager object, depending on the
API package:
If the Manager does not contain state information (the most common case), call the ServiceLocator's
getManager() method.
If the Manager contains state information, you must use an appropriate ManagerHome object to create the Manager.
(For example, you would use LearningOrderManagerHome to create a LearningOrderManager). In this
case, you would use the ServiceLocator's getHome() method to retrieve the ManagerHome, and use the
ManagerHome's appropriate method to retrieve the Manager.
To see which method to use, consult the API reference documentation and see if there is a ManagerHome class
corresponding to the Manager you want to create. If there is, use it to create the Manager. If not, use the
ServiceLocator.
Home Objects
If the package follows the Home/Entity model, use an appropriate Home object to manage Reference objects (and their
underlying data in the Saba data store). Each Home object provides three functions:
create(): Creates a reference object, storing its data in the Saba data store. Usually, the method is passed an
appropriate Detail object containing the data for the new object. However, some Home classes' create() methods
may be passed the information in different ways (for example, in a Vector, or as separate parameters).
findByPrimaryKey(): Finds the reference object with a specific primary key. (Each reference object is uniquely
identified by a primary key.)
delete(): Deletes the specified reference object, along with its underlying data in the Saba data store.
In addition, many Home objects provide other "find" methods, which find all reference objects which meet certain
criteria (for example, "find all active deliveries"). These methods usually return all matching objects in a Collection.
For details, see the API reference page for the appropriate Home object.
To get a Home object, call the ServiceLocator's getHome() method.
Entity Objects
If the package follows the Home/Entity model, use an Entity object to find or change a reference object's underlying
data (after first using the Home object to find the appropriate reference object). Entity objects have various different
methods to set and change the data. For example, many Entity objects have getDetail() and setDetail()
methods, which let you get or change several fields at once. In addition, some Entity objects provide methods to retrieve
or change a single field in the underlying data store (such as ClientEntity.getDisplayName(), which enables
you to retrieve the client's display name and not its other fields).
To get an entity object, call the reference object's getEntity() method.
Typical Tasks
There are several tasks which you might want to perform with the API objects. This section describes some of the
operations you might perform.
In each example, assume that you have already instantiated a ServiceLocator object, which you can then use to
create any necessary delegates. You might create a ServiceLocator this way:
ServiceLocator myLocator =
SabaLogin.authenticate(username, password);
You can call one of the ServiceLocator's static getReference() methods. This method constructs a reference
object from the primary key and the class object. It does not check to see if the primary key is valid, and thus, if the
data exists in the Saba data store. That makes this method preferable if you know that the key is valid, because you
save the cost of a networked database query.
For example, to find the Employee object with a particular primary key, you would call
Employee employeeRef =
(Employee) ServiceLocator.getReference(Employee.class,
empPrimaryKey,
To verify that the object exists (that is, that the primary key is valid), call the appropriate delegate object's
findByPrimaryKey method. For a particular class of reference object (for example, "Foo"), the appropriate
delegate object is usually either a Manager ("FooManager") or a Home ("FooHome") object. In either case, it is
created by the ServiceLocator.
For example, to create an employee reference object, you might write code like this:
EmployeeHome myEmpHome =
(EmployeeHome) myLocator.getHome(Delegates.kEmployee);
Employee employeeRef =
(Employee) myEmpHome.findByPrimaryKey(empPrimaryKey);
In this case, the delegate object verifies with the Saba data store that the primary key is valid, and the information
exists in the database. This makes the method much slower, but more appropriate if you are not sure that you have
a valid key.
Once you have the reference object, you can use it to find the detail information, or perform other operations on the
data.
It returns a Collection of Vectors, each of which contains various data fields for the employee (such as first name,
last name, object ID, etc.).
PartyManager myPartyMgr =
(PartyManager) myLocator.getManager(Delegates.kPartyManager);
Modifying an Object
In most cases, the package's Manager or Entity delegate provides methods for modifying an existing object. Follow
these steps:
1. Find the reference object corresponding to the data you wish to modify.
2. Retrieve the detail object for that reference object (by using the appropriate method in the Entity or Manager object).
3. Modify the detail object appropriately. (Be sure to change only the fields you want to change in the underlying data
store!)
4. Call the appropriate "set detail" method (again, provided by the Entity or Manager). Pass the reference object and
the modified detail object. The Saba engine updates the information in the data store.
By using an <xsp:logic> tag This is the preferred way to execute Java code which produces side-effects, but
does not produce any output in the model page.
By using an <xsp:expr> tag This is the preferred way to execute Java code which generates output other than an
XML node.
By using a <wdktags:execute> tag This is the preferred way to generate an XML node in the model page.
From a model object If an application page has a model object, Saba automatically instantiates the object when it
builds the page. Saba then calls several of the object's methods. For example, it calls a method to have the object
restore any state and/or handle passed parameters. In addition, you can write any additional methods in the object
that you like, then call the model object's methods from inside the application page (for example, from inside an
<xsp:logic> tag). For more information about using model objects, see Chapter 5, "Model Objects"Chapter 5,
"Model Objects".
Note that while <xsp:logic> is the preferred way to execute code which produces side effects without output, it is
perfectly acceptible to use one of the other tags if you want to produce side-effects as well as output. For example, if
you want to delete some information from the data store, you might do it with <xsp:logic>. If, on the other hand,
you want to modify data in the data store, you might choose to do it with <wdktags:execute>, and generate an
XML node containing the modified data.
<xsp:expr>
You can encode a Java expression in a WDK page by using an <xsp:expr> tag. When the model page is processed,
the Saba engine evaluates the Java expression, and replace the tag with the expression's value. This is the preferred way
to insert text into a model page.
For example, if a model page contains the node
<sum><xsp:expr>2 + 3</xsp:expr></sum>
the Saba engine processes it, resulting in this node:
<sum>5</sum>
That node would be available to view pages which drew on the model page.
If you want to insert an XML node, the preferred way is with a <wdktags:execute> tag.
<xsp:logic>
This is the preferred way to launch Java code which does not result in output on the model page. Any code within this
tag is executed. If you want to output results to the page, use either <xsp:expr> or <wdktags:execute>.
<wdktags:execute>
The <wdktags:execute> tag (described on page 3-24) enables you to run Java code which produces an XML node.
This tag identifies a particular command object (either directly, or by invoking a command manager object). Each time
the model page is loaded-that is, each time a control page which references the model page is viewed-the WDK engine
instantiates the command object and invoke its appropriate execute() method, passing the application page's HTTP
request and a visitor object, as well as any specified arguments. The command object can perform any appropriate tasks,
creating API objects and interacting with the data store as necessary. When the execute() method returns, the WDK
engine examines the passed visitor object to generate an XML node. That node is inserted into the generated model
page, replacing the <wdktags:execute> node.
Note: The command object is always passed the HTTP request object for the application page which contained
the <wdktags:execute> tag. This request object contains, as one of its parameters, the user's session key.
Thus, your command object does not usually need to use the SabaLogin object to generate a session key.
The most common function for a command object is to build and generate XML data (using the visitor object), which
is then inserted into the model page. For example, a command object might search for a particular detail object, use the
visitor to build up an XML representation of the object, then return. (If the detail object implements IXMLObject,
generating the XML node can be as simple as calling the Vistor object's visit method and passing it the detail object.
Otherwise, you might have to examine the fields one by one, and call appropriate visit methods for each one.)
Similarly, you might use a command object to generate an entire table of data. The command object might search for
all objects meeting certain criteria (say, all employees in a particular department). It could then use the visitor object to
visit each object in turn, generating a single, large XML node with a subnode for each detail object. The node would be
placed in the model page, and view pages could display the information in a table (perhaps by using a table widget).
While a command object must produce an XML node, that need not be its sole, or even its primary, function. You might
write a command object whose main job is to perform some operation on the data store, but which also generates an
XML node as output. (For example, if a node modifies some data, you might generate an XML node containing the
modified data.) You might do this even if you don't intend to display the data in the node. This leaves the option open
if another developer wants to modify the view page to show the data. It also can be very useful for debugging, because
one debugging option allows the developer to view the intermediary XML pages (and thus examine the precise nodes
returned by the command object).
Note: You can also turn a command object into a Saba Web Service. This enables you to publish the object's XML
output through a SOAP connection to remote clients. For more information about creating web services, see
***XREF web serv. appendix***
Notification Actions
The system administrator can configure Java to perform a notification action whenever a specified event occurs. (For
example, the administrator might set up an action which sends an email to a particular person whenever a new learning
event is created.) Notification actions are discussed in the Notifications Administrator Guide.
Saba ships with several predefined Java actions. In addition, you can write your own Java actions and have them installed
in the system. Once you have done this, the system administrator can associate them with system events, as with the
pre-defined notification actions. When the specified event occurs, Saba invokes the corresponding action object's
executeAction method.
To create a new notification action, follow these steps:
1. Write a new class which implements the com.saba.notify.IJavaNotificationAction interface. Place
the notification code in that class's executeAction method. (For more information about this interface, see the
Web API Reference.
2. Install the notification action class into the Saba application's class path. Register that notification action with Saba,
as described in the System Administrator Guide.
3. Using the Saba System Administration module, associate the notification action with any appropriate events.
Whenever the specified events occur, Saba finds an object of the appropriate class and invoke its executeAction
method.
Note: Saba makes no guarantee as to when the notification object are created, how long it exists, or when it is
destroyed. For example, Saba might choose to create a single instance of a notification action class, then call its
executeAction class whenever an appropriate event occurs. Conversely, Saba might choose to create a fresh
instance of the class every time the specified event occurs, call its executeAction method, then destroy the
object. You cannot make any assumptions about how long a particular notification object might persist after it
finishes executing its executeAction method.
Non-WDK Programming
The most common reason for using the Saba API is to modify or create Saba application pages. However, there are
circumstances in which you might want to execute API code on its own.
The most common circumstance is when you are testing new code. Indeed, you'll probably want to test all your code
with standalone Java applications, before you try integrating it with WDK model pages.
In addition, you might sometimes want to create special, stand-alone applications which use the Saba API. One
circumstance when this might be useful is if you need to perform a large, one-time batch operation on the Saba data
store. For example, if your company has a reorganization, you might need to assign a large number of employees to new
departments. The simplest way to do this might be to write a quick Java program which retrievees each affected user's
record, displays his or her information to the administrator, and lets the administrator choose a new department. For
such an operation, it might make more sense to write the program entirely in Java, and not use the Saba application page
interface at all.
The API example code shows how to do this. Create a test Java class with a main() method which generates an
authentication certificate, then tests your objects:
/* In a WDK page, you could let the Saba engine catch the
*/
try {
ServiceLocator locator =
SabaLogin.authenticate(kUsername, kPassword);
catch (Exception e) {
e.printStackTrace();
System.exit(0);
} /* main */
Chapter
7
Tools and Utilities
Topics:
This section describes several tools and utilities that are shipped with the Saba
Cloud product.
Description
-siteURL
The URL of the Saba Cloud site whose pages you want to compile. If not specified, default
site is used.
-d
-page
Path to a specific application page's control file, relative to the directory specified with the -d
parameter. If not specified, all application pages in that directory are compiled.
-srcRoot
Location of archive (.ear file) or directory containing application pages to compile. If not
specified, site's saba.ear archive is used.
-repository
Location of application server's directory where the compiled application pages should be
stored. If not specified, default directory is used.
Examples
Compile all pages in the default site's saba.ear file, and store the results in the default application server directory:
compilepages
For the system's Main site, compile all pages in the specified archive, and story the results in the specified directory:
echo
Chapter
8
Designing Portlets
Topics:
Overview
Writing a Portlet WDK Page
Coding a Portlet Manager
Installing a Portlet
Overview
Saba Learning enables users to set up portal pages, which can contain several special?purpose portlets. A portlet is a
small, self-contained application page that is designed to be displayed along with other portlets in a portal. Usually, a
portlet contains a small amount of information or lets a user perform a simple task (such as searching for something).
It should not contain too much information or be too complicated. For example, a "search for learning offering" portlet
might contain a single text box, where a user could type a text string. The portlet would then launch an application page
showing the search results.
A portlet is very similar to a WDK page. To design a portlet, you have to do the following things:
1. Create a WDK page for the portlet. This must be a control page, view page, and model page, as described in Chapter
3, "Scripting Customization (WDK)"Chapter 3, "Scripting Customization (WDK)". The control and view pages for
a portlet have some small special requirements. The model page is exactly like an application page's model page,
and indeed the same model page could be used by both a portlet and an application page.
Note: You can use any page that returns a well-formed xHTML fragment, instead of a WDK page.
2. Create a PortletPageManager class for the portlet. Usually this is done by extending the
AbstractPortletPageManager, providing an init method. This object specifies which WDK page is used
for the portlet. It can also, optionally, specify parameters and a help page for the portlet.
3. Install the portlet in Saba. This is done using the Portlets Creation Screen, as described in the System Administrator
Guide.
A control file, which specifies the model and view pages for the portlet, as well as any widget used by the view page.
A model file, which specifies the data presented by the web page, and specifies the widgets available for the page.
A view file, which specifies how the page presents its data.
The model file is precisely like an application page model file (and indeed, you could have a single model file be used
by both application pages and portlet pages). The control and view files are slightly different from application page
equivalents. The differences are described below.
For full information about authoring WDK application pages, see Chapter 3, "Scripting Customization (WDK)"Chapter
3, "Scripting Customization (WDK)".
Note: The portlet page does not have to be a WDK page. It can be any page which returns a well-formed xHTML
fragment. For example, it could be an xHTML file, or a CGI script which returns appropriate xHTML code. This
may be a good solution if you are writing a portlet which does not need access to the Saba data store (for example,
a stock-ticker portlet).
<?cocoon-process type="portlet"?>
(In an application page, the second line would contain type="wdk".)
In fact, if you change the type value to wdk, the portlet page becomes an application page, and can be launched and
tested like any other application page; as noted below in "Testing a Portlet Page", this is the preferred way to test a
portlet page before deploying it.
<xsl:template match="wdk:model">
cellspacing="1" cellpadding="0">
</table>
</xsl:template>
Again, the height and width should be specified as percentages, not as pixels.
If the link's <action> node (see page 4-83) is of type portal, Saba repaints this portlet, refreshing its contents.
Otherwise, the page called replaces the entire portal page.
Of course, you can always have the link open a popup window by giving it a <popup> child.
For example, every portlet has a particular page (a WDK application page, CGI script, etc.). If you wanted to define a
simple portlet with no parameters or online help, you could do it by creating a new portlet manager class like this:
extends AbstractPortletPageManager {
registerPage(PortletPageManager.kDefaultDisplay,
"/platform/presentation/portal/portlet/my_simple_portlet.rdf");
} //init
} //class
Saba can now create the object and query it to find out what the portlet's page is.
There are several things you can specify when you create a portlet:
The request which should return this page. In the future, we may support having portlet managers receive several
different requests, and being able to return a different page for each request. For this reason, specify which requests
should return this page. Currently, the only supported value is PortletPageManager.kDefaultDisplay.
The URL of the page associated with that request. This must be the absolute path to a file on this or another host. If
the portlet is a WDK page, this should be the path of the page's control file (.rdf).
registerPage(kDefaultDisplay,
"/platform/presentation/portal/portlet/MyCorpPortlet.rdf");
If the portlet opens an xHTML file named http://portlets.mycorp.com/portlets/stockTicker.htm,
you would make this call:
registerPage(kDefaultDisplay,
"http://portlets.mycorp.com/portlets/stockTicker.htm");
To add a help page, call the registerHelpURL method, and pass the URL of the help page. This must be passed as
an absolute path to a file either on that server or on another. For example, to register the help page (on the Saba server)
"/portlet_help/myPortletHelp.html", you would include this call in your init method:
registerHelpURL("/portlet_help/myPortletHelp.html");
To use a help page on another server, such as
"http://portlets.mycorp.com/portlet_help/myPortletHelp.html", you would include this call:
registerHelpURL( "http://portlets.mycorp.com/portlet_help/myPortletHelp.html");
Installing a Portlet
To install a portlet, follow these steps:
Make sure the portlet page manager is in Saba's Java class path.
Install the portlet page in the appropriate location (wherever you specified in the portlet page manager code).
Launch the Saba application's Create Portlet screen, as described in the System Administrator Guide. Follow the
instructions there on creating portlets.
Chapter
9
Writing Web Services
Topics:
Overview
Command Object Requirements
Generating a WSDL File
Installing a Custom Web Service
This chapter describes how to write a web service that publishes Saba information
through a SOAP connection.
Overview
Saba Cloud provides several web services that enable remote clients to read and alter Saba's data store through a SOAP
connection. The system ships with several web services installed. In addition, you can write additional web services,
providing any other data or features you need.
You create a new web service by writing a Saba command. Command objects are usually used with Saba application
pages; a command object can return XML data to be displayed by an application page, and can also make changes to
the data store or produce any other side-effects you need. By registering a command object as a Saba web service, you
can make the same functionality available to remote clients.
To create a new web service, follow these steps:
1. Write a command object which performs the desired functions. The object might retrieve a particular set of data,
modify the data store (for example, by creating or modifying an object), produce some other side-effect, or any
combination of the three. Compile the object and install it in the application server's class path.
2. If you want the web service to be available to through a SOAP connection, use the provided generateWSDL script
to create a WSDL (Web Services Description Language) file from the command object. Place the WSDL file on a
web server accessible to your clients (you can put it on the server you use to host Saba Cloud).
Note: Clients that use Saba's Java or C# interfaces do not need a WSDL file, though you may wish to create
one anyway to help document the web service.
3. Register the command object as a Saba web service by using the System Administration module's New Web Service
screen. If you want to provide a WSDL file, provide its URL here.
After you do this, clients can access your web service the same way they would any other Saba web service. For more
information about using web services, see the Web Services Developer Guide.
For full details about writing command objects, see Chapter 6, "Coding Customization (Java)"Chapter 6, "Coding
Customization (Java)".
For example, here is a simple web service command object which receives a single parameter, and returns that same
parameter serialized in an XML node:
/**
*/
super();
/**
* Serialize results.
<echo>input string</echo>
* @param request
* @param visitor
*/
throws Exception
} /* doExecute */
} /* EchoCommand */
Description
command_class
Fully-qualified class name of the command object which is used by the webservice. This object
must be in your Java class path
Parameter
Description
service_name
The name you use for the web service. Enter the same name when you register the service
through the System Administration module. The WSDL file generated is given the name
service_name.wsdl.
You must invoke this utility on the machine where Saba is installed (and where the class has been installed into your
application server's class path), and Saba must be running at the time. The utility prompts you to enter a user ID and
password for a Saba user account. It then prompts you to enter appropriate parameters for the command object. The
utility invokes the command object with those parameters, analyzes the XML output, and generates a WSDL file based
on that output. The file is written to the current directory, with the name service_name.wsdl (using whatever service
name you passed to java2WSDL.).
For example, if you want to use the class com.mycorp.myCommand to provide a service named myService, you
would invoke the command this way:
All parameters are defined in an xsd:sequence. That is, they are assumed to all be required. If any of the parameters
are optional, you must change the WSDL file to reflect this..
You may wish to replace some xsd:string elements with enumerated types (specified with xs:enumeration).
Commands that may generate different XML formats depending on their input values have only one possible output
captured in the generated schema (that is, whatever output format was produced by the parameters you provided to
java2WSDL). In that case, add the other possible output formats by hand. (You may wish to run java2WSDL
several times, with the different types of input values, then manually combine the WSDL files to produce a single
WSDL file which allows for any of the possible output formats.)
All attributes are given a datatype of xsd:string. You may be able to change these to more accurately reflect the
data types used.
You may wish to explicitly define xsd:ID types.
You may wish to add explicit minOccurs and maxOccurs attributes.
All schema definitions are generated with the default namespace. You can add namespaces explicitly.
Generated schemas are completely unmodular and extremely nested. You should consider extracting common
elements.
You may wish to change the target namespace.
The generated WSDL file does not have any comments. You should add documentation to make the WSDL file
easier to use.
For example, here is a fragment of the schema created by generateWSDL for the learner profile web service:
<xsd:element name="certification">
<xsd:complexType>
<xsd:sequence>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
and here is the equivalent fragment from the manually edited schema definition for this service:
<xsd:complexType name="certification">
<xsd:sequence>
<xsd:element name="status">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Acquired"/>
<xsd:enumeration value="Expired"/>
<xsd:enumeration value="Discontinued"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
Notice that the following changes were made:
The element definition was extracted into a top-level complexType definition to make it more modular
The type of id and targetDate was made more specific
The type of status was changed to a set of enumerated values
The schema was augmented with elements for startedOn and acquired. This is data that is potentially serialized
by the command, but happened to not be present in the example data provided for this run of the command.
Required?
Description
Name
Yes
Name of the web service. Java clients must pass this name to the
InfoServiceClient.callService method to access the
service.
Description
No
Java Class
Yes
No
URL of the WSDL file describing the service. This is not needed if
all clients use the Java InfoServiceClient interface.
Chapter
10
Localizable Objects
The following table provides a complete list of all localizable objects in the
Saba application, including the localizable attributes of each object:
Table B-3:) Localizable Objects (page 1 of 4)
Object
Localizable Attributes
Class ID
Product Group
Name
ddcls000000000001037
Offering Template
Title
Description
Abstract
Delivery Mode
Description
ddcls000000000001404
Delivery Type
Name
ddcls000000000001056
Description
Virtual Classroom
Abstract
ddcl
Description
Instructor-led Offering
Abstract
ddcls000000000001039
Description
WBT
Abstract
ddcls000000000001420
Description
Remote Lab
Abstract
Description
ddcls000000000001502
Object
Localizable Attributes
Class ID
Video On Demand
Abstract
ddcls000000000001444
Description
Simulation
Abstract
ddcls000000000001483
Description
Job Type
Name
ddcls000000000001072
Description
Role
Name
ddcls000000000001104
Description
Equipment
Name
ddcls000000000001051
Resource Inventory
Name
ddcls000000000001073
Description
Competency
Name
ddcls000000000001350
Description
Competency Levels
Name
ddcls000000000001351
Description
Competency Groups
Name
ddcls000000000001353
Description
Competency Criticality
Name
ddcls000000000001072
Description
Competency Evidence
Name
ddcls000000000041351
Description
Competency Method
Name
ddcls000000000041352
Model Group
Name
ddcls000000000001373
Object
Localizable Attributes
Class ID
Goal Metrics
Name
ddcls000000000006004
Description
Goal Types
Name
ddcls000000000006001
Description
Goal Actions
Name
ddcls000000000006012
Description
Goal Unit
Name
ddcls000000000006003
Description
Goal Status
Name
ddcls000000000006000
Description
Performance Method
Name
ddcls000000000001520
Description
Provider Type
Name
Description
Notification Event
Name
ddcls000000000001517
ddcls000000000001085
Description
UI Group
Name
ddcls000000000001361
Folder
Name
ddcls000000000001471
Certification
Name
ddcls000000000001330
Description
Certification Path
Name
ddcls000000000001378
Certification Module
Name
ddcls000000000001363
Object
Localizable Attributes
Class ID
Curriculum
Name
ddcls000000000001330
Description
Curriculum Path
Name
ddcls000000000001378
Report Category
Name
ddcls000000000001481
Description
Chapter
11
WDK Page Tutorial
Topics:
Overview
Writing a Simple Portlet
Using Widgets
Command Objects and Visitors
This chapter describes how to write a WDK application page. The chapter covers
a wide range of possible page types, from the very simple to the elaborate.
Overview
Saba application pages are written in our WDK format. This format is described in detail in Chapter 3, "Scripting
Customization (WDK)"Chapter 3, "Scripting Customization (WDK)". A WDK application page is made of three files:
The control file is the master file. It specifies the other two files used by the application page, assigns a unique
identifier number to the page, and specifies certain other high-level resources (such as the widget library used by
the page).
The model file specifies the data displayed in the application page, and also specifies which widgets are available to
the page.
The view file specifies how the data and widgets are arranged on the application page.
These files are all written in XML. The control file uniquely identifies the application page. That is, by specifying a
control file, you have specified an application page. By contrast, a single model file can be used by several different
application pages. For example, a single model file can present information about users, and several different view files
can present the model file's data in different ways. For example, one view file can display a table with all the data,
another can display only certain columns of the table, and another can display a stripped-down version suitable for
displaying in a portlet.
It is possible to use a single view file with several different model files, but this is far less common than using several
views with a single model. Usually, a view file is tailored to a specific model file.
The model file is compiled to create a Java class. This compilation is done once for each model file. Once the Java
class is created, it can be used every time a user opens an application page which uses that file. The compiled Java
class generates XML output, which contains the data displayed on an application page.
The view file is used to transform the XML output into the web page that is displayed by a user's brower. It produces
a web page with XHTML and Javascript. There is not usually any Java in the output, so the user's browser does not
need to be Java-enabled. (Of course, you could write an application page which runs a Java applet if you wished.)
The control file identifies the model file and view file used by the application page. When you want to add an
application page into the system, you do it by creating a link (or a menu item) which opens that control file; Saba
then uses the control file to open the view and model file.
Thus, when a user launches an application page, the system follows this sequence of events:
1. The system checks the control file to see what model file and view file are used by the application page.
2. The system checks to see if the model file has already been compiled into a Java class. If it has not, the system
compiles that model file.
3. The system creates a Java object from that class, and uses the object to generate the XML data used by the application
page.
4. The system uses the view file (and any associated stylesheets, like the widget definition stylesheets) to transform
the XML data into an XHTML/Javascript file.
5. The system sends that file to the user's browser, which displays the finished application page.
If the application page is a portal, the system also follows this sequence for each portlet on the page.
The way Saba constructs the finished application page has a few implications you should keep in mind:
The first time a person runs an application page, Saba has to compile the model file into a Java class. This greatly
slows down the application's response time for that page. The next time anyone views that page (even with different
data), Saba can use the cached Java class, greatly improving response speed. (You can also use a Saba utility to
compile all the application pages at once, instead of waiting for users to load the pages.)
When a user views an application page, Saba checks to make sure that the compiled model class is up-to-date. If the
model file is more recent than the Java class, Saba recompiles the class. However, some application servers keep
caches of Java classes used. As a result, if you change a model file while Saba is running, and the model file has
already been used since the last time the application server was launched, the server may use its cached version of
the class instead of the recompiled version based on the new model file. This keeps the changes from showing up
in the compiled application page. For that reason, after you make changes to model files, you may need to restart
your application server to view the changes. (This is not necessary if you make changes to the view or control file.)
You might create a brand-new page entirely from scratch. In that situation, you would need to create a control, model,
and view file, and insert the page into the application.
You might edit an existing WDK page. In that case, you might duplicate an existing page, modify the duplicate, and
insert the new page into the application. You could also edit the existing page in place.
If you are editing an existing application page, you can ignore much or all of what you see in the files and make a small
alterations. For example, you might decide that a particular table should show less information than it does. You could
do this by editing the view file to remove a particular column, and not altering the model or control files at all. On the
other hand, if you are creating a new page, you must understand how all three files are constructed, but you might not
need to know subtler details about how the components work. The page that you construct can be simpler than typical
Saba application pages.
Of course, there isn't always a clear line between creating new pages and editing existing ones. Usually when you create
a brand new application page, do it in stages, gradually building up the functionality from one draft of the page to the
next (and borrowing code from existing application pages that do similar things). That is how this tutorial is structured.
It begins by describing how to create a simple portlet, then describes how to add functionality to the portlet, turn it into
an application page, and then add advanced features.
Tutorial Description
This tutorial describes how to create a wide number of WDK pages. It shows how to build simple application pages
from scratch, and also how to modify simple pages to create more complex ones.
Note: You may never need to create an entire application page. Usually, you are able to design web pages by
modifying existing WDK application pages.
The tutorial covers the following steps:
but portlets should be small, simple, and not make assumptions about how much screen space they can have. This ensures
that Saba can resize the portlet as necessary, and still have it be readable when displayed.
Because a portlet is small and self-contained, and because it is easy to install into Saba, portlets are a very useful way
to design and test WDK functionality. For example, very often the meat of an application page is a table displaying
information. You can build and test that table in a portlet. Then, once that code is working, you can expand it into a full
application page.
This section covers the following steps:
1. Writing a portlet that displays simple text, installing it in Saba, and viewing it.
2. Expanding the portlet to use some common widgets, and to follow Saba's best practices.
<?cocoon-process type="portlet"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="511111">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version> <wdk:model
rdf:resource="helloWorldPortlet.xml"/> <wdk:view
rdf:resource="helloWorldPortlet.xsl"/> <wdk:widgets
rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
</rdf:Description>
</rdf:RDF>
The bold-face sections are areas you usually would need to change when you create a new control file. In addition, if
the model file contains links to model files (including itself), include a <wdk:links> node, as described in
"<wdk:links>""<wdk:links>" on page 3-19. Other than that, your various conrol files can all look pretty much the same.
The parts of the control file that might change include:
Cocoon process type: The <?cocoon-process> tag must have a type attribute. This attribute is set to portlet
for portlet pages, and wdk for ordinary application pages.
ID Number: Every application page (including portlet pages) must have a unique ID number. Saba reserves all ID
numbers up to 500,000. You can use any integer above that for your application pages. The ID number is specified
in the <rdf:description> node's id attribute.
Model and view pages: The control file specifies the model file and the view file used by this page or portlet. This
is specified either with the full URL of the model and view files, or (more often) with the path relative to the location
of the control file. Thus, if the model, view, and control files are all in the same directory (as is usually the case),
you can enter the names of the files.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdk:labels>
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
</wdk:widgets>
</wdk:page>
</xsp:page>
As you can see, this model file contains very little information. The only information specific to the portlet is the text
associated with the hello_text label, "Hello world!"
<?wdklint ignoreRules="XSL_HTML"?>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss10="http://purl.org/rss/1.0/">
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
</xsl:template>
</xsl:stylesheet>
The defaultview.xsl file is responsible for creating the general framework of the portlet, including an <HTML>
node. To fill in the contents of the <HTML> node, that stylesheet invokes the wdk:model template which you write.
The result is a portlet page containing a single <p> node, which contains the value of the hello_text variable:
<HTML>
cellspacing="1" cellpadding="0">
<tr><td>Hello, world!</td></tr>
</table>
</HTML>
Note: As noted in Appendix A, "Designing Portlets"Appendix A, "Designing Portlets", a portlet should consist of
either an HTML table (and its contents), or a Saba table widget. Thus, the "Hello, world!" text is put inside an
HTML table with one row and one column.
When you write the portlet page manager, you must know where the application page's control file is installed. (The
Saba application can then examine the control file to find the locations of the model and view files.) In this case, we
assume that the control file is installed as /tutorial/portlet/helloWorldPortlet.rdf.
package com.saba.client.portal;
import com.saba.exception.SabaException;
import com.saba.portal.AbstractPortletPageManager;
import com.saba.portal.PortletPageManager;
/**
* This class registers the page for the Hello World portlet.
*/
public HelloWorldPortletPageManager() {
/**
* registerPage.
*/
registerPage(PortletPageManager.kDefaultDisplay,
"/tutorial/portlet/helloWorldPortlet.rdf");
} /* init */
} /* HelloWorldPortletPageManager */
After you compile this class, install it into Saba. Place the class file in the Saba application server's Java class path, then
add the portlet and its manager using the Saba system administration module (as described in the System Administrator
Guide).
Create appropriate resource entries in that resource bundle. In this case, we assume that the resource bundle is named
"my_custom_pages", and we add a single entry to the default resource file:
kI18n511111helloWorldLabel=Hello, world!
Note the naming convention followed. All of an application page's resources should be given names that fit the pattern
kI18n<page_ID_number><label_name>
The model file can then load any resources it needs from that bundle. To use the value of that label, you would change
the model file described above to the following. (Once again, changes are in bold-face.)
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdktags:i18n.load resource="my_custom_pages"/>
by the view file -->
<wdk:labels>
<wdk:label name="hello_text"><wdktags:i18n.label
name="kI18n511111helloWorldLabel"/></wdk:label>
</wdk:labels>
</wdk:head>
<wdk:form method="POST">
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
</wdk:widgets>
</wdk:page>
</xsp:page>
When Saba compiles the Java code for the model page, it transforms the <wdktags:i18n.label> node into an
appropriate command to load the text from the resource bundle. Thus, the page uses whatever label is best suited for
the current locale.
The view and control files do not need to be changed to make use of the new label. The view file loads the contents of
the hello_text XML label, as before.
Using Widgets
The previous example showed how to create an application page or portlet which uses straight xHTML. You could use
exactly the same technique to display any other code which is supported by your target browser, such as Javascript or
Java applets.
Saba also provides a powerful library of widgets which you can use in your pages and portlets. There are several
advantages to using widgets:
The widgets provide a wide range of functionality, including special tools for accessing the Saba data store.
The Saba application automatically adjusts the widgets to match whatever theme and skin is in use.
Saba provides a syntax for dynamically creating as many widgets as necessary for the data you want to display. For
example, you might create a page which displays all students in a class, along with a "delete student" button for each
student. In that case, you could define the "delete student" widget once, then use WDK directives to instruct Saba
to create a copy of that widget for each row in the "students enrolled" table.
This section of the tutorial describes how to add a widget to the "Hello World" portlet defined in the previous section.
In this case, the portlet has a single widget, a textArea widget which can be used to display or input text. The following
section describes how to create multiple widgets dynamically to match whatever text your application page may be
displaying.
If you use a widget, you must insert code in two places:
The model file defines the widget itself, specifying what parameters are passed to the widget and how it should be
displayed.
The view file specifies where in the file the widget should be displayed.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdk:title>Hello World</wdk:title>
<wdktags:i18n.load resource="my_custom_pages"/>
<wdk:labels>
<!-- Label is no longer needed! -->
</wdk:head>
<wdk:form method="POST">
</wdk:labels>
<wdk:model>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:input name="helloText">
name="kI18n511111helloWorldLabel"/></value>
</wdk:input> </wdk:widgets>
<value><wdktags:i18n.label
<viewOnly>true</viewOnly>
</wdk:page>
</xsp:page>
As you can see, this version of the portlet no longer has a label. The view file doesn't display the text directly, by accessing
the label. Instead, it displays the helloTextwidget, which contains the message.
<?wdklint ignoreRules="XSL_HTML"?>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rss10="http://purl.org/rss/1.0/">
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
</table>
</xsl:template>
</xsl:stylesheet>
The bold-faced statement applies the standard Saba widget library's stylesheet to the selected widget. In this case, the
widget is identified by its name, but you could identify the widget by any other XSLT path. For example, you could
search for a widget with a particular ID, or for a widget that's the child of some other node.
This example uses a simple command object to generate the XML node. In this example, the command object generates
a static, fixed node, containing data which is displayed in the application page. Of course, the true strength of command
objects is that they can generate their XML output dynamically, and can retrieve information from the Saba data store.
In addition, this application page is designed as a stand-alone page, instead of as a portlet. There is very little difference
between writing a portlet and writing an application page. However, you must add the application page to the Saba
menus, instead of providing a portlet that users can add to their portal pages. For more information about adding new
application pages into the menu system, refer to the System Administrator Guide.
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:wdk="http://www.saba.com/XML/WDK">
<rdf:Description id="522222">
<rdf:type resource="http://www.saba.com/XML/WDK/Control"/>
<wdk:version>1.0</wdk:version> <wdk:model
rdf:resource="tutorialCommandPage.xml"/> <wdk:view
rdf:resource="tutorialCommandPage.xsl"/> <wdk:widgets
rdf:resource="/wdk/xsl/widget/wdk_widgets.xsl"/>
</rdf:Description>
</rdf:RDF>
In this case, we assume that the control file is in the same directory as the model and view files, and that all three files
have the root name tutorialCommandPage (with the various appropriate file extensions). We give this application
page the ID number 522222, and designate it as a standalone application page, instead of a portlet.
uses the simplest version of that directive, in which it specifies that a particular command object should be created, and
that command object's execute method should be invoked.
A command object must implement Saba's ICommand interface.Usually, it does this by extending the
AbstractCommand class. If you create a new class based on AbstractCommand, you must provide two methods:
You must write a no-argument constructor for the object. If the command object expects to be passed parameters,
the constructor method performs some initial processing. If the command object doesn't pass or return parameters,
you can write an empty constructor (as in this example).
You must write the doExecute method. This method is what generates the XML output. The method does this by
using a visitor. The visitor is a special-purpose object created by AbstractCommand's code, which produces XML
representations of Java objects. If the visitor examines a simple Java type (like a number or a string), it generates an
appropriate XML node. If it visits Saba object which implements IXMLObject, it generates a node for the object,
then recursively visits each field in the object. Thus, if you want to generate an XML representation of some Saba
data, all you usually need to do is create the appropriate Saba object, then pass it to the XML visitor.
In the case of this example, the command object builds its XML output a node at a time, creating a static data set.
/*
* TutorialCommand.java
*/
package com.mycorp.tutorial
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import com.saba.web.dk.AbstractCommand;
import com.saba.xml.IXMLVisitor;
import com.saba.xml.SabaXMLType;
/**
*/
*/
public TutorialCommand()
/**
* by node.
<people>
<person>
<last_name>Smith</last_name>
<first_name>John</first_name>
</person>
</people>
*/
IXMLVisitor visitor )
throws Exception
visitor.endVisit(null, "person");
visitor.endVisit(null, "person");
visitor.endVisit(null, "person");
visitor.endVisit(null, "people");
}
This object must be compiled, and the class file placed in your application server's class path.
Instead of containing the data directly in the file, the model file invokes a command object to generate XML data.
(It uses the command object defined in the last section, TutorialCommand.)
The model file uses a table widget to display the XML data. The model file attaches the table to the data generated
by the command object, by using a <wdktags:attachTo> directive. The table widget automatically displays as
many rows as are needed by the data.
<?xml version="1.0"?>
<xsp:page language="java"
xmlns:xsp="http://www.apache.org/1999/XSP/Core"
xmlns:wdktags="http://www.saba.com/XML/WDK/taglib">
<xsp:structure>
<xsp:include>com.saba.*</xsp:include>
</xsp:structure>
<wdk:page xmlns:wdk="http://www.saba.com/XML/WDK">
<wdk:head>
<wdktags:i18n.load resource="my_custom_pages"/>
</wdk:head>
<wdk:form method="POST">
<!-- Model information is in this tag. This is where we make the call to
<wdk:model>
<!-- In the output, the following tag is replaced
by
the XML node output from the command object.
-->
<wdktags:execute
commandObj="com.mycorp.tutorial.TutorialCommand"
/>
</wdk:model>
</wdk:form>
<wdk:widgets>
<wdk:table name="peopleTable">
<!-- The attachTo node
indicates that this widget is
linked to the 'people' node created by the
command object. -->
<wdktags:attachTo path="people"/>
<head> <!-Defines the heading row -->
<!-- Usually, you would use internationalizable labels for the heading row's labels.
In this example, we use
static text for
simplicity. -->
<column>First Name</column>
<column>Last Name</column>
</head>
<row path="person"> <!-- i.e. one
row for each
'person' node -->
<column>
<wdktags:nodeRef path="first_name"/>
</column>
<column>
<wdktags:nodeRef path="last_name"/>
</column>
</row>
</wdk:table>
</wdk:widgets>
</wdk:page>
</xsp:page>
The <wdktags:execute> directive invokes the command object; as we saw, that object generates the static XML
output
<people>
<person>
<last_name>Malfoy</last_name>
<first_name>Draco</first_name>
</person>
<person>
<last_name>Dumbledore</last_name>
<first_name>Albus</first_name>
</person>
<person>
<last_name>Weasley</last_name>
<first_name>George</first_name>
</person>
<people>
The model file also specifies that a table widget should be created, and attached to the <people> node. The table
widget specifies that it should have one row for each <person> node under the <people> node. Each row, in turn,
has one column for the first name and one for the last name. The widget thus generates a table like this:
First Name
Last Name
Draco
Malfoy
First Name
Last Name
Albus
Dumbledore
George
Weasley
Note that the table displays first name before last name, which is different from the order in the command object's output.
When you define the table widget, you are perfectly free to specify what order you want the data to appear in. You can
also choose to omit some of the data returned by your command object. For example, you could easily create a table
which displayed last names.
View File
This view file is very straightforward. All the model file's data is presented in a table widget. The view file needs to
display that widget.
<?wdklint ignoreRules="XSL_HTML"?>
<xsl:import
href="/wdk/xsl/view/wdk_defaultview.xsl"/>
<xsl:import
href="/wdk/xsl/view/wdk_widgetlayout.xsl"/>
<xsl:template match="wdk:model">
<xsl:apply-templates
select="$wdkWidget[@name='peopleTable']"/> </xsl:template>
</xsl:stylesheet>
Chapter
12
Tips and Tricks
Topics:
Overview
Making SQL Calls from a Client
Command
Using Dynamic Delegates
Extending Saba's EJBs
Extending the Saba EJB
Modifying ejb-jar.xml
This appendix describes several useful techniques for working with the Saba
Cloud platform.
Overview
This manual covers the basic techniques for writing application pages and Java code to interact with the Saba Cloud
platform. However, some coding tricks and techniques do not fall into any category and are useful in only a few situations.
This appendix describes several such techniques. This appendix is not intended to be a comprehensive guide to Saba
Cloud programming. Instead, it is a cookbook of techniques that our own engineers and consultants find useful.
The following code might be placed in a client command object to access the database. In this example, the code queries
the cmv_pub_smp_person view to find the first and last name of a particular person.
private String[] getName(Person person) throws SabaException {
String sql = //replace below with whatever SQL query you want
"select fname, lname from cmv_pub_smp_person where id=?";
String[] name=new String[2];
ConnectionManager cm=mLocator.getConnectionManager();
Connection conn=cm.getConnection(mLocator.getSabaPrincipal().getSiteName());
PreparedStatement stat=null;
ResultSet rs=null;
try {
stat=conn.prepareStatement(sql);
stat.setString(1,person.getId());
rs=stat.executeQuery();
if(rs.next()){
name[0]=rs.getString(1);
name[1]=rs.getString(2);
//System.out.println("value of name:"+name[0]+"
}
}
catch(SQLException x){
Debug.GetStackTrace(x);
x.printStackTrace();
throw new SabaException (x);
"+name[1]);
}
finally {
try {
rs.close();
stat.close();
cm.freeConnection(conn);
}
catch(SQLException x){
// do nothing
}
return name;
}
getServiceLocator().getConnectionManager().getConnection(
getSiteName());
For information about the ServiceLocator, DynamicSessionBean, and IDelegateDynamic classes,
consult the API reference documentation.
2. Put the compiled code in customizations.jar, and store that archive in saba.ear.
3. Obtain an instance of your object by calling the ServiceLocator.getDynamicDelegate() method, passing
it the name of your interface and class. You might do this from inside a command or model object, or from Java
code included in a model file.
All the code in that class is executed on a transactional basis. You can be useful, for example, when you run transactional
database code.
invokes its superclass method, then calls a new, private assignPerformanceReview method to provide the new
functionality.
/* Class which extends Saba's ActionPlanWorkflowEJB */
package com.sab
a.custom ;
import com.saba.actionplan.*;
import com.saba.actionplancycle.business.*;
import com.saba.exception.SabaException;
import com.saba.locato r. ServiceLocator;
import com.saba.customattribute.*;
import com.saba.review.*;
import java.util.*;
/**
* Customizes the standard ActionPlanWorkflowEJB to automatically create
* a review once the action plan is completed.
*/public class CustomActionPlanWorkflowEJB extends ActionPlanWorkflowEJB
{
super.moveActivatedActionPlanToCompleted(actionPlan,
actionPlanDetail);
// attach performance review when the action plan is completed
assignPerformanceReview(actionPlan, actionPlanDetail);
}
/**
* attach performance review when the action plan is completed
*
*/
private void assignPerformanceReview( final ActionPlan actionPlan, final
ActionPlanDetail actionPlanDetail ) throws SabaException
{
// get the action form for the action plan
ActionForm actionForm = actionPlanDetail.getActionForm();
ActionFormManager actionFormManager
= (ActionFormManager)
getServiceLocator().getManager(Delegates.kActionFormManager);
ActionFormDetail actionFormDetail = actionFormManager.getDetail(actionForm);
// check if the action plan has the review attached using a value stored in
a custom field
ICustomAttributeValueDetail custom1Value =
(ICustomAttributeValueDetail)actionFormDetail.getCustomValue("custom1");
if(custom1Value != null)
{
// get the review reference
String id = custom1Value.toString();
ReviewForm reviewForm = (ReviewForm)
ServiceLocator.getReference(ReviewForm.class, id);
// set the status, start date and end date of the review
ReviewDetail reviewDetail = reviewManager.getReviewDetail(review);
reviewDetail.setStartDate(actionPlanDetail.getStartDate());
reviewDetail.setEndDate(actionPlanDetail.getEndDate());
reviewDetail.setReviewState(ReviewState.kActive);
reviewManager.setReviewDetail(review, reviewDetail);
}
} /* assignPerformanceReview */
} /* CustomActionPlanWorkflowEJB */
Modifying ejb-jar.xml
Once you have created your new subclass, you must modify Saba's deployment descriptor file to call your new class.
find the reference to the existing EJB and change it.
For example, in this case we have written a new class named CustomActionPlanWorkflowEJB which extends
Saba's ActionPlanWorkflowEJB. The relevant portion of the ejb-jar.xml file is:
<session id="ActionPlanWorkflow">
<ejb-name>ActionPlanWorkflow</ejb-name>
<home>com.saba.actionplancycle.business.ActionPlanWorkflowBeanHome</home>
<remote>com.saba.ejb.IDynamicStatelessBean</remote>
<ejb?class>com.saba.actionplancycle.business.ActionPlanWorkflowEJB</ejb?class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type> <security-role-ref>
<role-name>sabauser</role-name>
<role-link>sabauser</role-link>
</security-role-ref>
</session>
We make the following change (marked in bold):
<session id="ActionPlanWorkflow">
<ejb-name>ActionPlanWorkflow</ejb-name>
<home>com.saba.actionplancycle.business.ActionPlanWorkflowBeanHome</home>
<remote>com.saba.ejb.IDynamicStatelessBean</remote>
<ejb?class>com.saba.custom.CustomActionPlanWorkflowEJB</ejb?class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<security-role-ref>
<role-name>sabauser</role-name>
<role-link>sabauser</role-link>
</security-role-ref>
</session>
Chapter
13
Sample Java Program
Topics:
The Saba application provides a sample of Java code which uses the Saba API.
The code is provided on the product CD in the
toolkit\developer\examples\java directory. In addition, the files
are listed here, with descriptions.
Besides demonstrating how various API classes work, the sample code can be useful as a skeleton for your own test
code. By modifying the main() method, you can have the program run other test methods which you might add to the
class.
The code must be compiled and run on a machine with a working Saba installation.
Files
The API sample contains the following files:
APIExamples.java This contains all the Java code. You need to modify some of the file's constants before
compiling.
build.bat A script for compiling the sample code. You need to edit this file to put in the full paths of the
saba.jar, sabares.jar, and ejb2.0.jar files.
APIExamples.java
Edit the constants kUsername and kPassword, giving them appropriate values for your Saba site. You should not
need to edit any of the other constants.
build.bat
Edit the file's set commands to contain the paths to saba.jar, sabares.jar, and ejb2.0.jar on your installation.
run.bat
Make sure the file's JBOSS_DIST variable is set to the installation's JBoss directory.
2. Run the script run.bat to launch Saba and run the sample code. The script launches Saba, then launch
APIExamples.
build.bat
@echo off
set _saba_jar=D:\buildJar\lib\saba.jar
set _sabares_jar=D:\cvswork\sabahcdm\libraries\sabares.jar
SET _ejb_jar=D:\cvswork\sabahcdm\libraries\java\lib\ejb2.0.jar
run.bat
@echo off
set _saba_jar=D:\buildJar\lib\saba.jar
set _sabares_jar=D:\cvswork\sabahcdm\libraries\sabares.jar
SET _ejb_jar=D:\cvswork\sabahcdm\libraries\java\lib\ejb2.0.jar
APIExamples.java
/*
===================================================================
Company Confidential
===================================================================
*/
import com.saba.locator.*;
import com.saba.exception.*;
import com.saba.security.SabaLogin;
import com.saba.party.*;
import com.saba.party.person.*;
import com.saba.party.organization.*;
import com.saba.i18n.*;
import com.saba.location.*;
import com.saba.domain.*;
import com.saba.function.*;
import com.saba.primitives.*;
import com.saba.xml.*;
import com.saba.security.*;
import com.saba.catalog.*;
import com.saba.learningoffering.delivery.*;
import com.saba.nlevel.*;
import com.saba.learning.registration.*;
import com.saba.order.*;
import com.saba.currency.*;
import com.saba.learning.order.*;
import com.saba.learningoffering.*;
import com.saba.offering.*;
import com.saba.learning.resource.facility.*;
import com.saba.calendar.timeperiod.*;
import com.saba.courselanguage.*;
import com.saba.learningoffering.sessiontemplate.*;
import com.saba.attachment.*;
import com.saba.finder.FinderIterator;
import java.io.*;
import java.util.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DateFormat;
/**
* 5.1. Some of these APIs are not yet public and are subject to change in
release 5.2 to
* simpler.
*/
private static final String kTitle = "Communication Skills for Career Growth";
= DateFormat.getDateInstance();
try {
All
kPassword);
s();
theExamples.browseCatalog(locator);
theExamples.orderClass(locator);
theExamples.viewProfile(locator);
theExamples.updateUser(locator, employee);
theExamples.createClass(locator);
catch (Exception e)
e.printStackTrace();
System.exit(0);
} /* main */
/**
* the user.
*/
System.out.println("start of createUser");
locator.getManager(Delegates.kPartyManager);
for an employee. */
Collection businessUnits =
partyManager.findBusinessUnitByName(kBusinessUnitName);
Iterator i = businessUnits.iterator();
if (!i.hasNext()) {
criteria.
Collection employees
"", // organization
"", // location
i = employees.iterator();
if (!i.hasNext()) {
if (location == null)
SabaLocale locale =
(SabaLocale) ServiceLocator.getReference(SabaLocale.class,
"local000000000000001",
"English");
SabaTimeZone timezone =
(SabaTimeZone) ServiceLocator.getReference(SabaTimeZone.class,
"tzone000000000000005",
Domain domain =
(Domain) ServiceLocator.getReference(Domain.class,
"domin000000000000001",
"world");
JobType jobType =
(JobType) ServiceLocator.getReference(JobType.class,
"jobtp000000000001001",
"Architect");
String employeeNumber="10075";
calculateDate.set(2002, 1, 1);
AddressDetail address =
"", // addr2
"CA", // state
"94065", // zip
"U.S.A."); // country
ContactInfoDetail contact =
"", // phone2
"", // fax
"bcrais@peacekeper.com"); // email
SecurityInfoDetail security =
"talyn"); // password
name,
security,
employeeNumber,
timezone,
businessUnit,
locale,
domain,
Gender.MALE
);
detail.setManager(manager);
detail.setLocation(location);
detail.setJobtype(jobType);
detail.setSsNo(ssn);
detail.setJobTitle(jobTitle);
detail.setAddressDetail(address);
detail.setContactInfoDetail(contact);
Employee employee =
(Employee) partyManager.createEmployee(detail);
security list. */
locator.getManager(Delegates.kSabaSecurityManager);
Collection securityLists =
securityManager.findSecurityListByName(kSecurityListName);
i = securityLists.iterator();
if (!i.hasNext()) {
Fortunately, the
securityManager.addMember(securityList, employee);
System.out.println("end of createUser");
return employee;
} /* createUser */
/**
* @param locationName
*/
ServiceLocator locator)
throws SabaException
Uses a remote
LocationHome locationHome =
(LocationHome) locator.getHome(Delegates.kLocation);
Iterator i = locations.iterator();
if (!i.hasNext()) {
return null;
return location;
} /* getLocation */
/**
*/
System.out.println("start of browseCatalog");
learning catalog.
*/
locator.getManager(Delegates.kCatalogManager);
DeliveryHome deliveryHome =
(DeliveryHome) locator.getHome(Delegates.kDelivery);
Iterator i = deliveries.iterator();
if (!i.hasNext()) {
deliveryIds.add(delivery.getId());
current user. */
condition.setTitle(kTitle);
condition.setLocation(location);
condition.setDeliveries(deliveryIds);
condition.setIsLearner(true);
FinderIterator results =
catalogManager.findOfferings(condition).getFinderIterator();
*/
while (results.hasNext()) {
printOffering(oneRow);
System.out.println();
locator.getManager(Delegates.kNLevelHierarchyManager);
i = folders.iterator();
while (i.hasNext()) {
System.out.println(folder.getDisplayName());
condition.setCategory(folder, true);
condition.setIsLearner(true);
catalogManager = (CatalogManager)
locator.getManager(Delegates.kCatalogManager);
results = catalogManager.findOfferings(condition).getFinderIterator();
count = results.size();
*/
while (results.hasNext()) {
printOffering(oneRow);
System.out.println();
System.out.println("end of browseCatalog");
} /* browseCatalog */
/**
Helper method to print out the results from the data returned by the
catalog search.
*/
if (location != null)
locationName = location.getDisplayName();
if (startDate != null)
formattedDate = mDateFormat.format(startDate);
/**
* Helper method to search for a specific offering by title.
*/
throws Exception
locator.getManager(Delegates.kCatalogManager);
Iterator i = catalogManager.findOfferings(condition).iterator();
String offeringID;
if (offeringData != null) {
offeringID = offeringData.getPrimaryKey().toString();
else
offeringID = null;
return offeringID;
} /* findOffering */
/**
*/
System.out.println("start of orderClass");
We use the
We then
create an order, add order items to it, and save the order.
LearningOrderManager. */
LearningOrderManagerHome learningOrderHome =
(LearningOrderManagerHome)
locator.getHome(Delegates.kLearningOrderManager);
locator.getManager(Delegates.kUserManager);
* Party. */
ServiceLocator.getReference(Employee.class, user.getId());
(employee.getEntity(locator).getDetail().getCompany());
ServiceLocator.getReference(SabaCurrency.class,
"crncy000000000000001",
"US Dollars");
ServiceLocator.getReference(Domain.class,
"domin000000000000001",
"world");
LearningOrderDetail loDetail =
billToParty, // billTo
employee, // contact
currency, // currency
domain, // domain
employee, // baseCustomer
null); // soldBy
System.out.println("creating order");
LearningOrderManager manager =
learningOrderHome.create(loDetail);
price, // price
offering, // part
try {
System.out.println("adding ILT");
manager.addToOrder(detail);
catch (SabaException e)
prerequisites.
" + e.getMessage());
detail = (OrderItemDetail)
price, // price
1, // required quantity
wbtOffering, // part
try {
System.out.println("adding WBT");
manager.addToOrder(detail);
catch (SabaException e)
" + e.getMessage());
System.out.println("saving order");
manager.remove();
LearningOrderServices learningOrderServices =
(LearningOrderServices)
locator.getManager(Delegates.kLearningOrderServices);
LearningOrderDetail orderDetail =
learningOrderServices.getDetail(order);
Iterator i = orderItems.iterator();
while (i.hasNext()) {
/* print out some detail information about each item in the order
*/
OrderItemDetail orderItemDetail =
learningOrderServices.getDetail(orderItem);
+ orderItemDetail.getItemNo()
+ orderItemDetail.getPart().getDisplayName()
System.out.println("end of orderClass");
} /* orderClass */
/**
*/
System.out.println("start of viewProfile");
locator.getManager(Delegates.kPartyManager);
Collection employees =
"", // organization
"", // location
Iterator i = employees.iterator();
if (!i.hasNext()) {
writer.flush();
locator.getManager(Delegates.kRegistrationManager);
Collection registrations =
registrationManager.getRegistrationsForLearnerH(emp);
i = registrations.iterator();
int count = 0;
while (i.hasNext()) {
RegistrationDetail registrationDetail =
registrationManager.getDetail(r);
count++;
printRegistration(registrationDetail, locator);
System.out.println();
System.out.println("end of viewProfile");
} /* viewProfile */
/**
*/
SabaException
locator.getManager(Delegates.kOfferingManager);
OfferingDetail offeringDetail =
manager.getDetail(offering);
String name =
offeringDetail.getOfferingTemplate().getDisplayName();
} /* printRegistration */
/**
*/
System.out.println("start of updateUser");
locator.getManager(Delegates.kPartyManager);
detail.
detail.setTerminatedOn(new Date());
detail.setStatus("Terminated");
partyManager.updateEmployee(employee, detail);
System.out.println("end of updateUser");
} /* updateUser */
/**
*/
System.out.println("start of createClass");
/* Step 1.
OTs have an
locator.getHome(Delegates.kOfferingTemplate);
ServiceLocator.getReference(Domain.class,
"domin000000000000001",
"world");
String title =
"Wormhole Navigation";
domain, // domain
title, // title
description, // description
"Wormholes:
null, // vendor
// abstract
true, // is published
false); // is test
OfferingTemplate ot = otHome.create(otDetail);
/* Step 2.
* specific language. */
createDefaultLanguage(locator,language, ot);
/* Step 3.
ServiceLocator.getReference(Delivery.class,
"eqcat000000000000004",
"Instructor-Led");
String acronym="WH";
ot,
acronym,
domain);
locator.getHome(Delegates.kDeliveryMode);
DeliveryMode dm = dmHome.create(dmDetail);
/* Step 4.
This is an instance
System.out.println("creating ILT");
locator.getManager(Delegates.kOfferingManager);
if (location == null)
ServiceLocator.getReference(Facility.class,
"fclty000000000001000",
"Hilton Suites");
Integer deliveryType =
ServiceLocator.getReference(Company.class,
"cmpny000000000001000",
ServiceLocator.getReference(SabaTimeZone.class,
"tzone000000000000005",
2, // length
tptDetail);
locator.getHome(Delegates.kSessionTemplate);
stHome.create(stDetail);
location,
startDate,
endDate,
zero, //maxIntConf
status,
null, // CSR
domain,
language, // language
facility,
vendor,
// vendor_id
description,
openEnroll,
closeEnroll,
null, // delivery
);
/* Step 5:
System.out.println("creating attachment");
ServiceLocator.getReference(SabaLocale.class,
"local000000000000001",
"English");
locale,
"http://www.mapquest.com/maps/map.adp?country=US"
+ "&address=2400+Bridge+Parkway&city=&state=&zipcode=94065"
+ "&homesubmit.x=44&homesubmit.y=16", // url
"Map to Classroom",
// description
true); // is private
This is
reference (in this case, our new ILT offering) that has
the attachment. */
AttachmentManagerHome attachmentManagerHome =
(AttachmentManagerHome)
locator.getHome(Delegates.kAttachmentManager);
AttachmentManager attachmentManager =
attachmentManagerHome.create(offering);
AttachmentBuilder attachmentBuilder =
attachmentManager.createHeader(atHeaderDetail);
attachmentBuilder.addURLAttachment(atDetail);
attachmentBuilder.save();
ee /* Now create a WBT. It will inherit from the same offering
* template. */
"eqcat000000000000005",
"WBT");
acronym = "WH-OL";
ot,
acronym,
domain);
dm = dmHome.create(dmDetail);
System.out.println("creating WBT");
WBTOfferingDetail wbtDetail =
new WBTOfferingDetail(
wbtNumber,
startDate,
endDate,
url,
domain,
vendor,
null, // manufacturer
description,
ot,
language
);
System.out.println("end of createClass");
} /* createClass */
/**
*/
} /* getTime */
/**
* offering template
*/
Language language,
OfferingTemplate template)
throws SabaException
locator.getHome( Delegates.kCourseLanguageManager );
CourseLanguageDetail detail =
CourseLanguage cl = manager.addCourseLanguage(detail);
/**
*/
throws Exception
locator.getManager(Delegates.kI18NManager);
Collection languages =
manager.findLanguagesByName(kLanguageName);
Iterator i = languages.iterator();
if (!i.hasNext()) {
return language;
} /* getEnglishLanguage */
} /* APIExamples */
"
Chapter
14
Security Configuration
Topics:
Overview
Notes on Saba Configuration
Using Alternate Login Modules
Web Server Single-Sign On
J2EE Single Sign-On
Overview
Saba provides a highly flexible security infrastructure by conforming to as many standards as possible. Using the JAAS
standard provides customers the option to use pretty much any authentication mechanism they choose. By conforming
to J2EE web based security, we can support single sign-on between two unrelated J2EE web applications.
However, in order to provide the extensive security features demanded by our enterprise customers, we provide a more
granular security mechanism through our proprietary security layer wrapped in the J2EE standards.
The following sections give an overview of the Saba specific features.
Special Users
There are two special Saba users: admin and guest. Each has a specific purpose.
admin is designed as a bootstrap super user. This particular user is intended solely as a system-level user that allows a
customer to always access the system in super-user mode. It is strictly for:
admin is not really intended to perform any normal administrative activities. Customers are expected to designate explicit
administrators to correspond to their business requirements.
The guest user is the unauthenticated user. It is used to initially enter the system.
Neither of these users exists in the person schema in Saba's database.
Description
Members
sabauser
sabaadmin
guest
Un-authenticated users
Security.xml
Security related options are configured in [Saba install directory]/properties/security.xml. The following
chart explains what each property is and when it is used.
Property Name
Description
Used When
useCompositePrincipalName
useSabaLoginModule
support42Certificates
Setting it to false implies that only the default site can be used. Saba implements multi-site functionality using a
composite principal. Multi-site functionality is not available in the J2EE standard.
A single username (for example, "guest") cannot have different preferred locales in two concurrent sessions. However,
can still support multiple desktops and a user can still have a distinct preferred locale.
Saba's proxy user feature is disabled.
Login Modules
Login Modules implement a standard interface (defined by JAAS) that let you easily configure different authentication
mechanisms for applications.
Saba ships with three Login Modules that you can use: com.saba.auth.SabaLoginModule,
com.saba.auth.standard.LDAPLoginModule, and com.saba.auth.standard.NTLoginModule.
SabaLoginModule uses the username and password stored in Saba's database to authenticate users. It is the default
login module. LDAPLoginModule can be used to authenticate against an LDAP server. (This includes Microsoft's
Active Directory, which can act as an LDAP server.) NTLoginModule can be used to authenticate using the NT or
Windows 2000 operating system.
In addition to these modules, you can also use any third-party login module or write your own custom one.
WebServerSSOLoginModule
This is the default authentication method. The Web Server Secure Sign-On (SSO) module uses the web server to create
a trust relationship with the user's browser. If this module is used, then a user who is properly logged in (and authenticated)
to the client computer is automatically logged in to Saba. The web server uses the NT Challenge/Response protocol to
securely verify that the user is authenticated on the client.
This module is discussed fully in "Web Server Single-Sign On".
LDAPLoginModule
This Login Module enables generic LDAP authentication while still maintaining multi-site functionality. In other words,
useCompositePrincipalName in Security.xml can be true.
This implementation communicates with the LDAP server using JNDI. Configuration of this adapter is done in the
[saba.install.path]/web/[site]/properties/ldap_auth.properties file.
The JNDI options include whatever options your LDAP JNDI provider supports. The mapping of JNDI options to the
property names in ldap_auth.properties is:
JNDI Property
Context.INITIAL_CONTEXT_FACTORY
initialContextFactory
Context.SECURITY_PROTOCOL
protocol
JNDI Property
Context.PROVIDER_URL
providerURL
Context.SECURITY_AUTHENTICATION
authenticationScheme
To authenticate a given user, this implementation constructs a distinguished name (DN) by either first connecting using
a default JNDI context and looking it up, or by using the following formula:
uidAttributeName: The name of the attribute that in the object containing the users that corresponds to the userid.
userContextDN: Contains the LDAP context from which to search for users when lookupUserDN is true.
Note that colons and equal signs are escaped using a backslash in the file, but not when viewed in SabaAdmin.
A sample properties file looks like this:
initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
protocol=
providerURL=ldap\://ldapServer\:389
authenticationScheme=simple
principalDNPrefix=uid\=
principalDNSuffix=, ou\=People, dc\=sampleco
lookupUserDN=false
adminUsername=
adminPassword=
userContextDN=ou\=People, dc\=sampleco
uidAttributeID=uid
NTLoginModule
This LoginModule enables authentication the NT and Windows 2000 operating systems. This can be used in place of
the default SabaLoginModule.
It works by placing sspauth.dll in a directory in the system path, and setting NTDomain property in "Web Utility
Variables" in SabaAdmin to a comma delimited set of domains to check for valid users.
For example, if the saba users were members of two domains, SALES and MARKETING, then you would set
NTDomain=SALES,MARKETING
If a user attempts to log in as "userone", this LoginModule checks the password against the NT users
"SALES\\userone" and "MARKETING\\userone".
Note that this is different from com.sun.security.auth.module.NTLoginModule. This login module passes
username and password to Windows for authentication, whereas Sun's authenticates the currently running NT user.
How It Works
Saba implements Web Server Single Sign-On by integrating several Active Server Pages (ASP), a login page that
automatically submits itself, and a JAAS login module, WebServerSSOLoginModule:
1. The HTTP Client issues a request for the J2EE web resource /Saba/Desktop. This request goes through IIS, and
the plug-in proxies any responses from the application server.
2. The application server sees that this web resource is protected by a security constraint in Saba's web.xml and that
the client is not yet authenticated with the application server. It sends a redirect to the client to go to the login page
defined in web.xml.
3. With other authentication mechanisms, the login page lets the user enter in a username and password so that the
client can submit these values and authenticate with the application server. With the Web Server Secure Sign On
authentication module, the login page redirects to authenticator.asp.
4. authenticator.asp creates an IIS session if there isn't already one, and saves the username of the user running
the HTTP client in its session object. (IIS gets the username by using NTLM.) It then sends an HTML form back to
the client, which on loading automatically submits itself to /Saba/j_security_check and passes the Window
client's username (in the j_username field) and the IIS session id (in j_password).
Note: The contents of j_username and j_password may include additional or different information as
well, but we can ignore it for the purposes of this example.
5. The application server then passes this username and password pair to the registered login modules. When the
WebServerSSOLoginModule gets this username/password pair, it submits a request to authorizer.asp on IIS
to confirm that this username is the one stored in the session object for the specified session id. If they match, the
login module confirms that the authentication was successful.
6. After a successful authentication, the application server redirects to the original request, only now the user is properly
authenticated.
Configure IIS
IIS must front the application server. For information about this, consult the application server's documentation.
Next, install the two ASP pages and set the security privileges for those pages correctly:
1. Create a new virtual directory using Internet Services Manager that can run scripts called "sso". Make sure the "Read"
and "Run Scripts" access permissions are checked.
2. Extract the pages authenticator.asp and authorizer.asp from saba.ear and copy them to the virtual
directory created. For example,
webpackager extract SabaSite\index\j2eesecurity\authenticator.asp
C:\temp
webpackager extract SabaSite\index\j2eesecurity\authorizer.asp
C:\temp
cd /d C:\temp
copy SabaSite\index\j2eesecurity\*.asp C:\Inetpub\sso
3. Set the security privileges of authenticator.asp to use Challenge/Response authentication. To do this:
a.
b.
c.
d.
4. Set the security privileges of authorizer.asp to accept anonymous connections. Note that this is the opposite
of the setting you used for authenticator.asp. To do this:
a.
b.
c.
d.
5. Customize authenticator.asp to match your system. Open up authenticator.asp with a text editor,
and modify the parameters at the top- sabaAppContext and thisMachine- to match your system.
sabaAppContext is the application context used when deploying saba.ear, which is "/Saba" by default.
thisMachine is the host name that the login module uses to connect to this IIS machine.
Using WebServerSSOLoginModule
The default installation of Saba configures the application server to use the WebServerSSOLoginModule. For more
information about configuring login modules, see "Using Alternate Login Modules".
3. Set Trusted Servers to list the servers which the WebServerSSOLoginModule can trust. This should be set to
a comma-delimited list of server names. In web server cluster configurations, explicitly list each web sever host
here. If there is only one web server, then enter that one host name. Note that the names must match the names set
in the theMachine variable in authenticator.asp. (as described above in "Configure IIS".)
4. Set Trusted Domains to list the Windows domains the login module can trust. Any authentication attempts fail for
a user who does not belong to one of the listed domains.
5. Set Web Server Protocol to the protocol that the login module uses to communicate with the Authorizer Page. If
the browser client is using SSL, it is not necessary for the login module to also use SSL unless IIS is configured to
only accept SSL connections, or secure session IDs are used. However, if Web Server Protocol is set to https://, the
login module establishes a different SSL connection to IIS than the client uses. Thus it is impossible to use SSL with
secure session IDs and this single sign-on solution. Secure session IDs should not be necessary as long as all links
used in Saba use SSL to communicate with the server.
Two different applications (for example, an operating system and Saba) share a common authentication mechanism.
The same userID/password combination works for both applications.
Two different web applications (for example, a Sales Force Automation (SFA) application and Saba) share a common
authentication mechanism AND session information. A user logs in once and can access both applications and Saba
in a single browser session without having to log into each application separately.
To do the former, read about using alternate Login Modules discussed above. This section describes how to accomplish
the latter. To be successful, all applications sharing session authentication information must be fully J2EE compliant.
In particular, this means supporting web based security settings in the web.xml deployment descriptor and supporting
JAAS.
To do this, look in the web.xml deployment descriptor of saba.ear to get the complete list of roles required by saba.
Copy and paste those into other application's web.xml. Do the same for all of the other application's roles. For example:
<security-role>
<description>Guest privileges to the Saba application</description>
<role-name>guest</role-name>
</security-role>
<security-role>
<description>Any valid Saba user including guest</description>
<role-name>sabauser</role-name>
</security-role>
<security-role>
<description>Saba Administrator</description>
<role-name>sabaadmin</role-name>
</security-role>
Security Constraints
The servlet spec does not specify what happens to currently authenticated user if that user navigates to a URL that is
not protected by a security-constraint element in web.xml. Some application servers drop the authenticated user entirely.
It is therefore important that all URLs are protected with a security constraint. By default, this is the case with Saba.
Chapter
15
Eclipse Plug-in
Topics:
This chapter describes the Saba plug-in for working with WDK pages in Eclipse.
The control file is the master file. It specifies the other two files used by the application page, assigns a unique
identifier number to the page, and specifies certain other high-level resources (such as the widget library used by
the page).
The model file specifies the data displayed in the application page, and also specifies which widgets are available to
the page.
The view file specifies how the data and widgets are arranged on the application page.
All three files are written in XML. You can write Saba application pages with any XML editor or with any text editor.
However, as a convenience, we have provided a plug-in to make it easier to write and edit WDK pages from inside
Eclipse, a popular third-party application development environment. This plug-in makes Eclipse's built-in text editor
more useful for working with Saba application pages.
Note: The plug-in supports only Eclipse versions 2.1.2, 2.1.3, and 3.0. The plug?in does not support any other
versions of Eclipse.
Installing
The plug-in is provided in the developer directory of the product distribution as a ZIP archive. This file has a name like
com.saba.wdk.xmleditor_version_number.zip
To install the plug-in into your Eclipse installation, follow these steps:
1. Shut down Eclipse.
2. Use any ZIP file editor to open the archive. Install the archive's contents into your Eclipse installation directory's
plugins directory. (For example, if you installed Eclipse into d:\Eclipse, you would extract the archive into
d:\Eclipse\plugins.) Make sure your ZIP extractor creates the subdirectories specified by the archive. There
is probably a setting with a name like "Create Subdirectories" or "Use folder names".
3. Verify that the files were installed properly. The ZIP extractor should have created a subdirectory of plugins
named com.saba.wdk.xmleditor_version_number.
4. Restart Eclipse. Eclipse should automatically detect and install the plug-in. You can verify that the plug-in has been
installed by opening Window->Preferences. There should be a new Preferences section named WDK Editor.
If you ever need to reinstall the plug-in, shut down Eclipse, delete the old
com.saba.wdk.xmleditor_version_number directory, then follow the installation steps described above.
1. Open the appropriate environment script found in the same directory as webPackager. (That is, if you are on
a Windows machine, open environment.bat. If you are using Unix, open environment.sh.) Verify that
the various environment variables are set appropriately for your system.
2. Call the webPackager script with the appropriate parameters. To extract files from the Saba installation, use the
following syntax:
saba_dir: The location in the Saba resource of the files you want to extract. For example, stylesheets are located
in SabaSite\wdk\xsl\view; application pages are stored in various subdirectories of SabaSite
extract_location: The directory where the extracted files should be installed. webPackager creates any needed
subdirectories.
Thus, the command to extract all the stylesheets on a Windows installation and install them to d:\myCustomPages
would be
Configuration
There are some configuration options that you must set:
The plug-in associates RDF files with itself. (Saba control files use the extension .rdf.) If RDF files are not already
associated with another program, you can use the Saba WDK plug?in by opening an RDF file with Eclipse. If you
have already associated RDF files with another program, however, that program takes precedence. To change this,
follow these steps:
1.
2.
3.
4.
5.
If you do not want to make Saba's plug-in the default editor, you can still use it by right-clicking an RDF file and
choosing Open With->WDK Page Editor.
You may also wish to associate XML files in general with the plug-in. If you do this, you can open model files
directly, instead of having to open the control file which uses the model file. To do this, follow these steps:
1.
2.
3.
4.
You must configure your Saba site to load the project's custom pages. To do this, follow these steps:
1. Log in to Saba with an account with system administration privileges. Open the Saba System Administration
module. Navigate to the screen System Administration->System Configuration->Sites.
2. Select the site you wish to configure.
3. Open the Web Variable screen for that site.
4. Set the Alternative Source URL property to the value
<project_path>/SabaSite
where <project_path> is the URL of the root of your Eclipse project. The URL should use the file:/ prefix,
indicating that files are loaded from the local file system. For example, if your project stores its files in
C:\customPages on the Saba server, you would set the Alternative Source URL property to the value
file:/c:/customPages/SabaSite
There are several Preferences settings for the WDK plug-in. If you want to use the Preview functionality (described
in "Preview Pane"), you must edit one of these. You can edit the preference settings by opening the Eclipse
Preferences window (by choosing Window->Preferences), and selecting the WDK Editor section.
The available preferences settings are:
WDP Page Editor preference settings
Field
Description
Mode
Indicates how the Eclipse plug-in should run. Most people should use Customizer (indicating
that the plug-in is customizing an existing Saba installation).
This documentation assumes you are using the Customizer mode. It does not support using
the Developer mode (intended for people developing and compiling the Saba application).
Schema URL
The location of the XML schema describing Saba model pages. The plug-in uses this to find
the documentation for model page nodes. Usually, you should set this to the URL of the
documentation XML file in your saba.jar file, such as:
resource:com/saba/wdk/xmleditor/documentation.xml
However, if this URL is not valid, the plug-in uses the copy of the schema stored in the
plug-in's JAR file. (However, this may result in the plug-in using documentation which is
out of sync with your installation.)
Application URL
If you want to use the Preview functionality (described in "Preview Pane"), enter the URL
of your application server in this field. For example, if Saba is installed on a host named
sababox, this might be
http://sababox/Saba/Web/Main
Temp directory
Field
Description
Asset URL
Location of Saba static assets (graphics, etc.), expressed as a URL. If you have a running
Saba installation, this is usually http://server_name/Saba/assets.
Path to the directory where the plug-in writes custom resource files. These files are created
when you modify the labels for a WDK page, as described in "Labels View".
This path is relative to the Eclipse project root.
Locale
Location of saba.jar
Full path of the Saba.jar archive. The plug-in retrievees resource information from this
bundle. This archive is found in the Saba installation's lib directory. Thus, a typical value
for this field might be d:\SabaWeb\lib\saba.jar
WDK page directory Path to directory where extracted WDK pages are stored. This path is relative to the Eclipse
project root.
Preview: Used to show the edited application page. This is described further in "Preview Pane".
To edit any file, open the appropriate pane and edit it normally. The WDK editor behaves as a normal XML editor. For
example, when you type an opening XML tag (like <i>), the editor automatically inserts the corresponding ending tag
after the cursor (in this case, </i>). In addition, its Model pane provides extra functionality, which is described below.
Note: If you choose File->Save while a WDK page is open, all files associated with the page are saved, not only
the file in the currently active panel. For example, if you open a WDK page and edit its control pane, then switch
to the model pane and edit that, then choose File->Save, both the control file and the model file is saved.
The Eclipse plug-in also provides a Labels window, which you can use to view and edit the labels used by the application
page. If you edit the labels, the plug-in creates a new Java resource containing only the edited labels. You can install
this resource to change the labels in your application. The Labels view is discussed thoroughly in "Labels View".
You can also use the plug-in to open model files directly, instead of opening the control file which uses the model file.
(This can be useful it you want to extract and modify a model file, and don't need to make changes to the control and
view files for that application page.) To do this, you must associate the WDK plug-in with *.xml files (as described
in "Configuration"). If the WDK plug-in is the default editor for *.xml files, you can edit them by double-clicking
them from the project view. If it is not the default editor, you can still launch it by right-clicking on the model file and
choosing Open With->WDK Modelpage Editor. If you open a model file directly, all the functionality described in
"Model Pane" is available. However, the plug-in does not show a Control, View, or Preview pane.
Model Pane
The model pane enables you to edit the application page's model file. This pane functions as a regular text editor, with
several additional features:
Code Assist: Shows appropriate XML tags for whatever context you're in.
Documentation View: Lists the appropriate child nodes for whatever context you're in, along with a brief description
of each node.
Outline View: Lists all the widgets in the model page in a hierarchical view.
Code Assist
While you are editing a model file, the WDK editor can show you appropriate tags for whatever context you are in. This
only happens when you are inside the file's <wdk:page> node.
If you type a < character while inside the <wdk:page> node, the WDK editor shows a pop-up list of tags that can be
used in your current context. For example, if the cursor is inside a widget node and you type <, the editor shows a list
of child nodes which can belong to that widget.
Note: Some valid child nodes are not displayed in the list. This is a known defect of the editor. If a child node's
tag is not listed, type it in.
If you select a node from the list, the editor inserts it and the node-closing tag into the document. For example, if you
choose <action> from the list, the editor inserts the text <action></action> at the cursor's location, and put
the cursor between the two tags.
The model pane also provides code-assist for Java elements within the model page. For example, when you type the
name of an object or class, followed by a period, the pane shows a pop-up menu listing methods provided by that object
or class. In addition, if you right-click on the name of a Java method and choose Open Declaration, the plug-in opens
a Java window containing the Java code in which that method is declared.
In addition, both XML and Java elements are color-coded to make them easier to read.
Documentation View
The WDK editor provides a Documentation view for use when editing model files. To open this window, choose
Window->Show View->Other, then select WDK Views->Documentation View.
If your cursor is inside the <wdk:page> node, but is not inside an XML tag, the documentation view lists the child
nodes which can be used at your current location. (This is the same as the list of tags which would pop up if you typed
a <.) In addition, the window shows a brief description of each tag.
Note: There may occasionally be valid child nodes which are not displayed in the list. This is a known issue with
the editor.
Outline View
While you are editing a WDK page, the Eclipse Outline view displays the hierarchy of widgets in the page's model file.
(It displays this regardless of which pane you may have displayed.) The outline view shows the XML hierarchy headed
by the model file's <wdk:widgets> node. However, the outline view ignores any <xsp:logic> tags in the file. It
does not ignore the contents of those tags. It acts as if the <xsp:logic> tag was not there, and as if that tag's children
were children of the <xsp:logic> node's parent.
For example, suppose your model file has the following hierarchy:
<wdk:widgets>
<wdk:link />
<xsp:logic>
<!-- some Java code... -->
<wdk:file />
<!-- more code... -->
<wdk:tree />
<xsp:logic />
</wdk:widgets>
In this case, the Outline view shows a <widget> node with three children, the link, file, and tree widgets.
You can further expand any node in the outline view to see its child nodes. The outline view shows all XML attributes
of each node. It does not, however, show the contents of the nodes. That is, it does not show the text between XML tags,
it shows the tags themselves.
If you click on any node in the outline view, the model pane scrolls to the corresponding code.
Preview Pane
The Eclipse plug-in can display the application page in a preview pane. There are two preview modes:
Static: The plug-in uses the assets URL to generate a static representation of the page. This is the default mode.
To view a static representation of a page, open the WDK page and click the Preview tab at the bottom of the window.
A static preview of the page appears, showing you the location of all the widgets on the page.
Dynamic: The plug-in dynamically compiles the page and shows you a running instance, using the Saba application
server. To view the dynamic preview, open the Preview pane and click the Dynamic Preview button (which looks
like the Saba "S" logo):
To use dynamic preview, you must also configure the plug-in to use the appropriate application server, as described in
"Configuration". In addition, you must be editing the application page that is used by that application server, rather than
a copy of the page stored somewhere else.
When you launch a dynamic preview, the plug-in may prompt you for your Saba user ID and password. After you enter
this, it displays the page that you are editing.
Note: Dynamic preview shows only the modified application pages if you have configured Saba's Alternative
Source URL property to point to the location where the edited application pages are stored. Otherwise, the preview
pane displays the application pages stored in the saba.war file.
When you first launch a dynamic preview, it shows the Saba login page. After you log in, you can navigate to the location
of the page you are editing. The preview pane shows the most recently saved version of the page. Thus, if you make
edits to the page and save it, you can reload the preview pane to see the newly-saved version of the file.
Note: If your application server uses cached versions of the page, the preview pane may not show the most-recently
saved version. Consult your application server's documentation for information about disabling page caching. You
can always force the server to use the current version of the page by shutting it off and restarting it.
Labels View
The labels view enables you to see and change the labels associated with the application page.
The labels view assumes that you follow a particular naming convention. Every control file has an ID number, in its
<rdf:Description> node's id tag. The plug-in assumes that all of an application page's labels have names of the
form
Note that editing labels from this window does not make changes to the resource installed in saba.jar. Instead, when
you make edits to the label view, the WDK editor creates a new resource file, in the custom root directory you specify
(as described in "Configuration"). It follows these rules for modifying labels:
If you modify a label which exists in the saba.jar resource, but does not exist in the custom resource, the plug-in
creates an entry in the custom resource file containing the key and value you specify. (If necessary, it creates the
custom file.) The original value of the label is unchanged in the resource in saba.jar.
If you modify a label which already exists in the custom resource file, the plug-in changes the value in the custom
file to the value you specify.
If you create a new label by clicking on --NEW key-- (or by modifying the key of an existing label), the plug-in
makes an entry for the key in the custom file.
If you delete a label which exists in the custom file, the plug-in removes its line from the custom property file.
Note: If the label exists in both the saba.jar resource and the custom resource, it is deleted only from the
custom resource. The window then shows the label with the value it has in saba.jar, the way it does with
every other label which is found in saba.jar and not in the custom resource.
If you try to delete a label which exists in saba.jar but not in the custom resource, it has no effect. Because the
resource in saba.jar is not changed, the window retrieves the label from the default resource bundle again.
Once you have finished making changes to the labels, follow these steps to install them to your Saba installation:
1. Create a ZIP file containing the custom resource files. You can do this with any ZIP utility. Make sure to preserve
folder names. The resource files are in a directory in the custom bundle root path you specified (with the Preferences
window, as described in "Configuration").
Note: While creating the zip file make sure that path of all resource bundles in the zip file starts with /res.
For example: /res/com/saba/client/res/common_labels.properties.
2. Navigate to your Saba installation's bin directory. (For example, if Saba is installed in your server's d:\SabaWeb
directory, navigate to d:\SabaWeb\bin.)
3. Launch the command runPatch.bat (Windows hosts) or runPatch.sh (Unix hosts) from that location. Use
the following command options:
Description
-p
The localization patch file, including the path if necessary. For example,
-p d:\temp\custom_resource.zip
-l
Provide a name and location where runPatch can write a log file. For example,
-l d:\temp\saba_de_AT_patch_install.log
If that file already exists, it is overwritten.
4. When the script has finished, restart the Java application server.
| Index | 433
Index
<$nopage>i18nsee internationalization 15
<$nopage>platformsee application platform 14
A
application platform 14
3-tier architecture 14
application platformdescription 14
architecture 14, 15
common business objects 14
diagram 15
architecturedescription 14
architectureinternationalization 15
architecturesecurity 15
attachments widget 66
I
IM presence widget 154
initializing widgets from request object 60
input widget 155
instant messaging presence widget 161
internationalization 309
list of localizable objects 309
internationalizationdescription 15
internationalizationformat localization support 15
internationalizationlocalizable objects 16
internationalizationmutliple language support 15
J
JavaScriptusing a widget to insert into application pages 213
B
bread crumb widget 68
C
chart toolbar widget 77
chart widget 70
common business objects 14
description 14
component picker widget 81
component widget 84
contact information xi
custom fields widget 89
L
link widget 162
linksdelete link widget 94
linksframework link widget 147
linkslink widget 162
linkssave link widget 212
list of values (LOV) picker widget 184
list widget 178
localizable objects 309
complete list 309
data nodes 58
date input widget 91
delete link widget 94
domain picker widget 95
N
name attribute 58
P
F
file widget 98
finder control widget 144
finder query widget 100
finder result widget 115
form widget 146
framework link widget 147
functionality widget 149
G
generic text widget 150
H
hidden field widget 153
HTTP requestinitializing widgets from 60
S
Saba picker widget 210
save link widget 212
script widget 213
security 15
system architecture 14
| Index | 434
T
table widget 217
text area widget 228
time input widget 230
tree widget 232
V
validator widget 257
W
WDK pages 62
widget library 62
wdktagsattachTo 59
widgets 51, 62
defined 51
list of available widgets 62
widgetsattachments 66
widgetsattachTo subnode 59
widgetsbreadCrumb 68
widgetschart 70
widgetschartToolbar 77
widgetscomponentPicker 81
widgetscomponentWidget 84
widgetscustomFields 89
widgetsdata subnode 58
widgetsdateInput 91
widgetsdeleteLink 94
widgetsdomainPicker 95
widgetsfile 98
widgetsfinderControl 144
widgetsfinderQuery 100
widgetsfinderResult 115
widgetsform 146
widgetsframeworkLink 147
widgetsfunctionality 149
widgetsgenericText 150
widgetshiddenField 153
widgetsid subnode 58
widgetsimpresence 154, 161
widgetsinitializeFromRequest subnode 60
widgetsinput 155
widgetslink 162
widgetslist 178
widgetslovPicker 184
widgetsmultistep 187, 188
widgetspageText 190
widgetspageTitle 191
widgetspageWidget 195
widgetsparameters 205
widgetspresence 206
widgetsprompForSave 208
widgetssabaPicker 210
widgetssaveLink 212
widgetsscript 213
widgetstable 217
widgetstextArea 228
widgetstimeInput 230
widgetstree 232
widgetsvalidatorWidget 257
widgetsviewOnly subnode 61