The project I currently work on involves accessing SharePoint's Discussion Board from a desktop applications. This includes not only reading discussions but also creating new ones as well as replying to existing posts. Unfortunately as I quickly found out, there is no dedicated web service for dealing with this type of lists similar to Imaging or Meetings. So the only option was to use the the generic Lists web service. However this poses several problems that I want to share.
Reading discussion lists
If you read items from any discussion list using familiar GetListItems method all you will get are the top level discussion topics, similar to the default view if you view such list in browser. But how do you get all other messages that were posted as replies to these discussions?
To answer this question first we need to look at the configuration of such list.
As you can see this list has two distinct Content Types: Discussion and Message. Let's look at the first one:
Do you notice something odd here? This was actually surprising for me, but it turns out that Discussions are actually Folders! This means that we will probably find the Messages if we look inside particular folder. This makes sense if you think of it.
Ok, so how you read contents of a folder? It also took me while to find but the GetListItems method has additional parameter for QueryOptions. This is also XmlNode but it can contain Folder element that indicates with subfolder you want to read. In this case to get messages from discussion you need to pass the Url of the top level discussion as folder name like this:
XmlDocument doc = new XmlDocument();
XmlElement query = doc.CreateElement("Query");
XmlElement viewFields = doc.CreateElement("ViewFields");
XmlElement queryOptions = doc.CreateElement("QueryOptions");
XmlElement folder = doc.CreateElement("Folder");
folder.InnerText = discussionUrl;
XmlNode nodeListItems = service.GetListItems(listName, viewName,
query, viewFields, null, queryOptions, null);
This will return list of Messages. Basically all you need is the Body, Author and Created date but it has some other useful properties that you can use. For example DiscussionTitle will give you the subject of the discussion thread for the message and IsRootPost is another way to distinguish messages from top level discussions. The Discussion item contains same properties as well as one useful property called ItemChildCount that gives you a number of replies in a thread.
One very important property is Threading because it allows you to construct the threaded view of the discussions. It contains a text string that might seem garbage at first but if you compare these strings for several messages you might notice a pattern here. Basically for each reply this string starts with the Threading property of the parent messages and appends some suffix (I couldn't figure out how this suffix was constructed though). If you sort all messages on the Threading property, you can easily find out at what level the Message should be placed by comparing these string (I can post a simple recursive method to construct a tree view from this if anyone needs it).
Creating discussion items
So how do you add new add new items to the Discussion Board? Let's try with the UpdateListItems method from Lists web service. Following code will add new top level discussion item to specified discussion list.
XmlDocument xmlDocument = new XmlDocument();
XmlElement updatesNode = xmlDocument.CreateElement("Batch");
XmlElement methodNode = xmlDocument.CreateElement("Method");
XmlElement titleNode = xmlDocument.CreateElement("Field");
titleNode.InnerText = subject;
XmlElement bodyNode = xmlDocument.CreateElement("Field");
bodyNode.InnerText = body;
using (ListsWebService.Lists service = CreateListService())
XmlNode resultNode = service.UpdateListItems(listName, updatesNode);
Ok, this part wasn't hard. But how do I reply to this discussion? And here my friends is where I got stuck right now....
I've tried to use several properties to force the Message into certain Discussion/Folder. Both ParentFolderId and FileDirRef have no effect. The only way I could do it is by using BaseName property. But it requires to specify the name for the new item in advance in form <parent_folder>/<new_item> so there is always risk that it won't be unique (I hoped that SP would generate this for me). Another problem is that even though the new items are placed inside Discussion their Content Type is Discussion instead of Message, so the thread starts looking strange. I've tried setting both ContentType and ContentTypeId but it didn't work either. I've also tried setting the FSObjectType to 0 (zero) as it should indicate this is normal item not a folder.
Another problem I need to solve is how to construct the Threading property for the new item for this message to appear in correct order. I can't figure out how the suffix for the new message is constructed.
If there are any SharePoint specialists out there reading this I would really appreciate your help :-)
Please continue to my second article to learn how I solved these problems.