Adding Collaboration Services Using the AquaLogic User Interaction Development Kit
Pages: 1, 2, 3

Rendering the Task Tree

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:

  • Retrieve all tasks and sub-tasks in a task list.
  • Design the look and feel for the task tree display.

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"/>

1. Creating the task tree display

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();
}

2. Retrieving task lists and tasks

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;
}

3. Displaying task entries

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:

  • First, DataRow information is bound to the DataTable by calling DataTable.Rows.Add().
  • Second, after all tasks are appended, set DataGrid.DataSource=DataTable, followed by a call to DataGrid.Bind(), as seen in the last two lines in the CreateTree() method above.

4. Using the object details URL

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("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
  }
  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);
}

Finding a matching discussion: Query discussions

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.

Rendering 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:

  • Create a discussion/message DataRow.
  • Fill the columns with appropriate details, such as discussion/message name, details URL, and ID.
  • Attach the row to the Discussion 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.

Retrieving messages

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;
}

Creating a discussion

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;
}

Creating a discussion message

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;
}

Creating a reply to a discussion 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;
}

Conclusion

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.

References

  • IDK Javadoc and NDoc
  • .NET Web Controls FAQ
  • AquaLogic User Interaction Development Support Center

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.