Articles
Enterprise Architecture
Adding Collaboration Services Using the AquaLogic User Interaction Development Kit
Pages:
1,
2,
3
Now let's turn to rendering the task list, creating something like that shown in Figure 1. After all the tasks and sub-tasks are created, two steps are needed to render the task tree:
The task tree is rendered using a
DataGrid, which allows more control for how the data is displayed. (See
ASP.NET Documentation on how to use the
DataGrid control.)
<asp:DataGrid id="TasksDataGrid" runat="server" Width="100%"
BorderWidth="0px" ShowHeader="False"/>
The
CreateTree() method is responsible for creating the task tree display. It sets up the Data Grid with three columns: a checkbox column, a task name column, and an invisible id column. It then calls the
ITaskListManager.GetTaskLists() method to retrieve all the task lists and call the render method for each task list.
private void CreateTree(IProject project) {
TaskTableData.Columns.Add(new DataColumn("Name", typeof(string)));
TaskTableData.Columns.Add(new DataColumn("ID", typeof(int)));
ITaskList[] tasklists = GetTaskLists(sampleProject);
for (int i = 0; i < tasklists.Length; i++) {
AppendTasklist(tasklists[i]);
}
TasksDataGrid.Columns.Add(new CheckBoxColumn());
TasksDataGrid.DataSource = TaskTableData;
TasksDataGrid.DataBind();
}
The
ITaskListManager.GetTaskLists() method retrieves all task lists in a project. Additional options can be set on the task list filter and the task filter to narrow the results, such as the Task Completion Type (that is, to return pending tasks, completed tasks, or overdue tasks) and Assigned-to Type (that is, to return tasks that are assigned).
public ITaskList[] GetTaskLists(IProject project)
{
ITaskListFilter filter = tasklistManager.CreateTaskListFilter();
ITaskList[] children = tasklistManager.QueryTaskLists(project, filter);
return children;
}
Similarly, the
ITaskList.GetTasks() method retrieves all tasks in a tasklist:
public ITask[] GetTasks(ITaskList tasklist)
{
ITaskFilter filter = tasklistManager.CreateTaskFilter();
ITask[] tasks = tasklistManager.QueryTasks(tasklist, filter);
return tasks;
}
In this example, I use separate methods to render each task list and task.
For each task list, I use the
AppendTaskList() method shown below to find all its tasks and sub-tasks. Then the
AppendTask() method is called on each task to render each task as a row in the Task
DataGrid:
public void AppendTasklist(ITaskList tasklist) {
ITask[] childTasks = GetTasks(tasklist);
for(int i = 0; i < childTasks.Length; i++)
AppendTask(childTasks[i]);
}
The
AppendTask() method is used to render individual tasks. Each task is represented by a
DataRow in the
DataGrid. The
NAME column contains the task name and the task details link, and the
ID column contains the task ID. The binding of the information is done in two steps:
DataRow information is bound to the DataTable by calling
DataTable.Rows.Add().
DataGrid.DataSource=DataTable, followed by a call to
DataGrid.Bind(), as seen in the last two lines in the
CreateTree() method above.
The remote API provides retrieval of the details URL for all objects. The object details URL is convenient when you want to link back to the Collaboration UI for more details on the object. For example, if you want to send notifications to users when certain objects are updated, you can use the details URL in your email notification. In the following implementation of
AppendTask() I have appended the detail URL of each task as a click-through link at the end of each task:
public void AppendTask(ITask task) {
DataRow taskRow = TaskTableData.NewRow();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < task.Level; i++) {
//For look & feel
//indent subtasks according to their levels
sb.Append(" ");
}
sb.Append(task.Name);
sb.Append("<a href=\"" + task.DetailsURL + "\">");
if (task.Level == 0)
sb.Append(" [view task details]");
else
sb.Append(" [view subtask details]");
sb.Append("</a>");
sb.Append("<br/>");
taskRow["Name"] = sb.ToString();
taskRow["ID"] = task.ID;
TaskTableData.Rows.Add(taskRow);
}
Each assignment question (a top-level task) is created with a discussion for gathering ideas and conversations related to that particular question. The next step is to find out which task is selected by a user and retrieve the corresponding discussion.
Using the .NET
CheckBox and
DataRow controls, you can determine which question (that is, top-level task) is selected by the user. See
ASP.NET Documentation on how to use the
CheckBox and
DataRow controls.
After the user marks a question with the checkbox and selects what action he or she wants to perform, the next step is to find out the corresponding Discussion for that task. In EDK 5.1, a text-searching ability is not supported for querying discussion, therefore a string comparison is used here to find the discussion with a matching name.
private IDiscussion GetMatchingDiscussion(String taskName) {
IDiscussion matchingDiscussion = null;
IDiscussionFilter discussionFilter =
discussionManager.CreateDiscussionFilter();
IDiscussion[] discussions =
discussionManager.QueryDiscussions(sampleProject, discussionFilter);
for (int i = 0; i < discussions.Length; i++) {
if (taskName.Equals(discussions[i].Name)) {
matchingDiscussion = discussions[i];
break;
}
}
return matchingDiscussion;
}
This method can be used to retrieve the matching discussions for a selected task. The next step is to construct the discussion tree.
To render the discussion tree, you can reuse the approach for rendering the task tree. The main step is to create a
DataGrid with columns for checkbox, message names, and IDs. For details, refer to the explanation above for the
CreateTree() method.
Besides the
CreateTree() method, other methods to reuse include the
AppendTaskList() and
AppendTask(). The methods
AppendDiscussion() and
AppendMessage() can be customized to do the following:
DataRow.
DataTable.
Let's look at the other, more interesting Discussion API methods that are necessary to construct and render the discussion tree you'll need in order to create the Newsgroup View portlet.
All messages in a discussion or a project can be searched using the overloaded method
IDiscussionManager.QueryDiscussionMessages(). Additional options can be set on the message filter to narrow the results, such as the Status Type (that is, to return all, approved, or unapproved messages) and Moderator Type (that is, to return messages that are in a moderated discussion).
public IDiscussionMessage[] GetMessages(IDiscussion discussion) {
IDiscussionMessageFilter messageFilter =
discussionManager.CreateDiscussionMessageFilter();
DiscussionMessageQueryOrder queryOrder =
new DiscussionMessageQueryOrder(DiscussionMessageAttributes.Modified, true);
IDiscussionMessage[] messages =
discussionManager.QueryDiscussionMessages(discussion, messageFilter);
return messages;
}
When a new task is created in the Assignment Page, a corresponding discussion will be created for that task. The helper method below can be used for this purpose, setting the name of the discussion to be the task name:
public IDiscussion CreateAndStoreDiscussion(IProject project, String name, String description) {
IDiscussion discussion = discussionManager.CreateDiscussion(project, name, description);
//Additional task properties can be set before calling Store(),
//such as notes, risk, status, and security information.
discussion.Store();
return discussion;
}
The following code shows how to create a top-level discussion message (that is, a topic message) from an existing discussion. The method
IDiscussion.CreateDiscussionMessage() will create a new discussion message, and optional fields can be set on the message before persisting the message object with
IDiscussionMessage.Store():
public static IDiscussionMessage CreateAndStoreMessage(IDiscussion discussion, String subject, String body) {
IDiscussionMessage message = discussion.CreateDiscussionMessage(subject, body);
//Optional discussion message properties
message.Approved = true;
message.Description = "Option description for discussion message";
message.Store();
return message;
}
The code below shows how to create a reply message. A reply message is also an
IDiscussionMessage, but instead of creating it from the
IDiscussion object, you'll create it from an existing
IDiscussionMessage object.
Another thing to note is that unlike creating a top-level message, the call to create a reply message will create a persisted message, that is, no
Store() method is required to be called to persist the message after the create call, unless additional properties are set, as we do below:
public static IDiscussionMessage CreateAndStoreReply(IDiscussionMessage parentMessage, String subject, String body) {
IDiscussionMessage reply = parentMessage.CreateDiscussionReplyMessage(subject, body);
//Store() needs to be called to persist additional properties
//for a reply message
reply.Description = "Optional description for reply message.";
reply.Approved = true;
reply.Store();
return reply;
}
The IDK remote APIs offer a rich set of functionality for remotely interacting with the different products and services in the ALI Application suite. In particular, the Collaboration API focuses on exposing the collaborative features on information gathering and sharing using the ALUI Collaboration Service. You can extend your applications by embedding one or more of these features using the API, such as discussions, task lists, documents, and subscriptions.
Other advantages of using the IDK remote APIs include configurable logging support (see IDK Logging for more information) and easy integration with other ALUI applications such as the portal, Knowledge Directory service, Publisher service, and Search service.
Caroline Leung is a senior software engineer at BEA�s Business Interaction Division. She has been with the Developer Experience team for over four years and is responsible for cross-platform and remote API design.