Geeks With Blogs

News
View Szymon Kobalczyk's profile on LinkedIn

Szymon Kobalczyk's Blog A Developer's Notebook

In my previous post I wrote how you can access the contents of discussion boards using web services. For reading existing discussions threads the Lists web service was all that we needed. However there were problems when creating discussions using the same service. After whole day of trial and error I finally gave up this approach and decided to first try using SharePoint's object model instead.

On the side note, I was using our test server over Internet with was fine for working with web services. However to use object model I needed to have SharePoint locally so I could run the code directly on the server. For this I used Virtual PC machine with Windows 2003 Server and Windows SharePoint Services 3.0 installed. On this machine I've setup remote debugging so I could compile and debug the code in Visual Studio on my host machine while running it on the server.

So first I've tried to simply create new SPListItem with the Items.Add method on the corresponding SPList object. But although I tried setting several different properties I still got the same results as with Web Services. The only difference was that I somehow managed to get correct Content Type for messages.

While searching for some clue I also found an interesting benchmark from Michael Ekegren that compares various methods for inserting items to lists. Clearly using object model to add to Items collection has the worst performance.

After a bit more of searching I've finally found that in order to insert discussion items properly I must use two special methods on the SPUtility class. The CreateNewDiscussion static method creates a new discussion thread and the CreateNewDiscussionReply method creates a reply to existing thread. So I figure there has to be some magic involved for handling discussion if there are special methods just for this purpose. Later I found a blog post that shows how it could be done without using SPUtility.

With the help of these methods I could quickly construct an additional Discussions web service to add the missing functionality. It has two methods that simply wrap the calls to corresponding methods on SPUtility and map the parameters.

[WebMethod]
public XmlNode CreateNewDiscussion(string listName, string subject, string body)
{
    SPList list = FindList(listName); 
    if (list == null)
        throw new ArgumentException("List name is invalid.");

    SPListItem discussion = SPUtility.CreateNewDiscussion(list.Items, subject);
    discussion[SPBuiltInFieldId.Body] = body;
    discussion.Update();

    // this is only to ensure the Xml can be generated
    object t = discussion[SPBuiltInFieldId.ThreadIndex];

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(discussion.Xml);
    return doc.DocumentElement;
}

[WebMethod]
public XmlNode CreateNewDiscussionReply(string listName, int parentId, string body)
{
    SPList list = FindList(listName);
    if (list == null)
        throw new ArgumentException("List name is invalid.");

    SPListItem parent = list.GetItemById(parentId);

    SPListItem reply = SPUtility.CreateNewDiscussionReply(parent);
    reply[SPBuiltInFieldId.Body] = body;
    reply.Update();

    // this is only to ensure the Xml can be generated
    object t = reply[SPBuiltInFieldId.ThreadIndex];

    XmlDocument doc = new XmlDocument();
    doc.LoadXml(reply.Xml);
    return doc.DocumentElement;
}

First method CreateNewDiscussion requires name of the discussion list, subject and body for the new discussion thread. The CreateNewDiscussionReply also takes the name of the discussion list, the Id of the parent message (the one you reply to) and the body of the reply. Both methods return the XmlNode with the data from the new item to reproduce the behavior of the Lists service's UpdateListItem method. You might notice a small hack where I read some property of the new item before converting it to XmlNode. I found that this is required in order to regenerate the Xml property. Without this extra call this property would always return null.

I wanted to allow the list name to be either it's Title or ID just like in the methods of the Lists WebService so I'm using a helper FindList method to do the mapping.

private SPList FindList(string listName)
{
    if (String.IsNullOrEmpty(listName))
        throw new ArgumentNullException("listName");

    SPWeb site = SPContext.Current.Web;
    SPListCollection listCollection = site.Lists;
    try
    {
        Guid listID = new Guid(listName);
        foreach (SPList list in listCollection)
        {
            if (listID == list.ID)
                return list;
        }
        return null;
    }
    catch
    { }

    foreach (SPList list in listCollection)
    {
        if (String.Equals(listName, list.Title, StringComparison.InvariantCultureIgnoreCase))
            return list;
    }

    return null;
}

This method first tries to convert the list name to a Guid and find the list by it's ID. If the string is not a proper Guid it tries to find the list by it's name instead. Notice I've used the static String.Equals overload to do the comparison in invariant culture and ignoring case.

There are some additional steps that you need to perform to install this web service on SharePoint server. Follow the steps outlined in the nice walkthrough on creating custom web services in the documentation for WSS SDK .

So with the help of this additional service I finally managed to finish my first assigned task on the TSRI project. Below you can see a screenshot from the application showing the preview window for the discussion annotation.

Users can also reply to and edit individual discussion messages right in the application.

I will write more posts soon on the interface implementation (starting with the control templates for the glass toolbar).

Posted on Friday, March 16, 2007 10:01 AM Development , SharePoint | Back to top


Comments on this post: How to access SharePoint's Discussion Board using Web Services. Part 2.

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hey Man,
I can title you as God.... i have been searching for the exact thing what you have done.Even i am supposed to use these Discussion Boards in my web application.

Jesus you saved my time :-) .... Thanks a ton. If possible please mail me the necessary source code.I need to complete my requirement in 3 more days. Thanks Again.


Thanks,
Hani
honey008@gmail.com
Left by Hani on Mar 21, 2007 12:37 PM

# Links (3/19/2007)
Requesting Gravatar...
.NET C# regex email address Convert Array List to DataTable Describing Types in .NET Basic C# coding
Left by Member Blogs on Apr 25, 2007 9:45 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hey ...

Thanks a lot man ... you blog was really useful for me, otherwise it was supposed to be a big burden for me, for creating the Discussion in 2007.. actually I am working on migrating from 2003 to 2007..

Thanks a lot
May God Bless you ...


Regards
Arun.
Left by Arun on May 17, 2007 6:21 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Thank you so much...

This is exactly what I was looking for. The threading problem was causing me a big pain in the backside!

AdamK
Left by AdamK on Jul 11, 2007 11:21 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi,

Great Post!!!...I was able to create a new discussion forum if not exists and able to create a discussion reply for the same. But i couldn't get to know about the CAML querying the discussion board to get all the items and want to render into the Tree view in my web application. How do you did for your application showing all the discussion items in the tree view (looks like exapand and collapse kind of).? Please help me in this regard. This is urgent!!.

Thanks,
Kathir
Left by Kathir on Aug 21, 2007 1:50 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
I have a custom column named 'category' in the Discussion list
How can I provide the value to it using SPBuiltInFieldId?
trying like this
discussion[SPBuiltInFieldId.Category] = Category; but ending up with "Invalid field name. {6df9bd52-550e-4a30-bc31-a4366832a87d}" exceptions?
how to map the custom column value using SPBuiltInFieldId?
Thanks
Gopi
Left by Gopi on Nov 30, 2007 9:27 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
hi,

Thanks for the great help !!
I'll greatly appreciate if you help me to solve this problem:
I have used your webservice to export dicussions from an access database to MOSS, for the first few times (around 374 items) the webservice works fine , after that it starts giving error
"operation is not valid due to the current state of the object"..plz help!!
Left by Vivek on Dec 31, 2007 10:44 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi,

Thanks a lot Szymon Kobalczyk.

I would like to know how did you do Tree View of discussion board items. I have struggled a lot but couldn't find any hint on this.

Appreciate your help.

Thanks in advance.
Left by Jagdish on Aug 18, 2008 3:08 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
How to get the parentID?
Left by Miguel on Jun 01, 2009 6:55 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi Szymon Kobalczk,

This great. I hope I can get help from you.

I am working on similar task. I followed your suggestions and I have created a additional webservice and published it on Windows 2003 Server IIS 6.0. And calling the Webmethod CreateNewDiscussionReply at my client devlopment environment (Windows XP- VS 2008 - a WPF appln) by adding this addition webservice class. But I am getting an error - Object reference not set to an instance of an object.

I just create a Webservice application in Windows2003 Server and VS 2008 with mentioned two webmetods, and trying to call this Web service in my local system (XP- VS2008). I think I missed out some more functions in Service appln. Code snippet for the webservices is as follows:

public class NewLists : System.Web.Services.WebService
{
ListsWS.Lists listService = new ListsWS.Lists();

[WebMethod]
public XmlNode CreateNewDiscussion(string listName, string subject, string body)
{
SPList list = FindList(listName);
if (list == null)
throw new ArgumentException("List name is invalid.");

SPListItem discussion = SPUtility.CreateNewDiscussion(list.Items, subject);
discussion[SPBuiltInFieldId.Body] = body;
discussion.Update();

// this is only to ensure the Xml can be generated
object t = discussion[SPBuiltInFieldId.ThreadIndex];

XmlDocument doc = new XmlDocument();
doc.LoadXml(discussion.Xml);
return doc.DocumentElement;
}


[WebMethod]
public XmlNode CreateNewDiscussionReply(string listName, int parentId, string body)
{
SPList list = FindList(listName);
if (list == null)
throw new ArgumentException("List name is invalid.");

SPListItem parent = list.GetItemById(parentId);

SPListItem reply = SPUtility.CreateNewDiscussionReply(parent);
reply[SPBuiltInFieldId.Body] = body;
reply.Update();

// this is only to ensure the Xml can be generated
object t = reply[SPBuiltInFieldId.ThreadIndex];

XmlDocument doc = new XmlDocument();
doc.LoadXml(reply.Xml);
return doc.DocumentElement;
}

private SPList FindList(string listName)
{
if (String.IsNullOrEmpty(listName))
throw new ArgumentNullException("listName");

SPWeb site = SPContext.Current.Web;
SPListCollection listCollection = site.Lists;
try
{
Guid listID = new Guid(listName);
foreach (SPList list in listCollection)
{
if (listID == list.ID)
return list;
}
return null;
}
catch
{ }

foreach (SPList list in listCollection)
{
if (String.Equals(listName, list.Title, StringComparison.InvariantCultureIgnoreCase))
return list;
}

return null;
}
}


And I cam calling this webmethods from my clinet dev env is as follows:

NewListsWS.NewLists ExtListService = new NewListsWS.NewLists();

XmlNode DisNode = ExtListService.CreateNewDiscussionReply("Team Discussion", 9, "Testing the Discussion repl");


Please help to advise where I have done mistake.

Or please help to send the complete code for additional Discussions web service class to understand throughly.

Thanks in advance
MK
Left by MK on Jul 07, 2009 3:18 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi Szymon Kobalczyk,
you are the man. really i repect your effort, have you tried to do the same thing from outlook too. as i am working on outlook reply posting to the discussion board. as you have used windows forms,
i want to male discussion board as google or yahoo group as when you reply to the group id a mail is sent to all users and it gets published in the Froum it self.

my question is how discussion board works with mails. what is the parameter it takes from the mail so that it can distinguish between the mails of same subject. and when you reply the same mail it gets posted in as reply in the discussion board .

Thanks in advance
Left by Vikash Sharma on Jul 29, 2009 1:30 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
This actually can be done through SharePoint Web Services without custom services on the server using the AddDiscussionListItem method on the Lists web service. I have discovered several tricks for using this method. First of all, the message has to be MIME encoded, with the following header fields:
Message-id
Thread-index
Subject
Mime-version
Content-type

This MIME text needs to be converted into an array of bytes to call Lists.AddDiscussionBoardItem. The key to creating a reply is in the Thread-index message header. If the call is made a second time with the same thread index, the second call will create a reply to the topic created by the first call.

It took me forever to figure this out. Doesn't anyone else out there use SharePoint Web Services?
Left by Ram Sudama on Sep 10, 2009 10:23 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi Ram, thank you very much for this encouraging message. I tried Lists.AddDiscussionBoardItem with MIME packed message with headers. It creates the first message in the discussion perfectly. But any time I use Thread-Index header, it fails with the exception "Invalid length for a Base-64 char array". The message seems reasonable but the same call without Thread-Index creates a new discussion thread pretty well. Did you have this error while you were trying this method? Maybe the list should be configured in some special way? How to avoid it to create a reply? Thank you very much, Mark
Left by Mark Klinchin on Sep 10, 2009 9:17 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi Szymon,

Great posts! Will it be a problem if a message content contains links linking to a document library with pdf files? If user clicks a link to a pdf file, will user be able to view it? Thanks.

Andrew
Left by Andrew He on Nov 18, 2009 10:25 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
I am in need of the code using which I can create threaded view for a given post so that I can display it in the format required by our client
Left by Vijayalakshmi on Mar 24, 2010 4:07 AM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
If possible please mail me the necessary source code.I need to complete my requirement in 3 more days. Thanks Again.
Left by HuongSang on Jun 14, 2010 11:12 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
can u tell complete steps to do this
Left by Sharmila on Aug 27, 2010 12:20 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Szymon,
I have been searching all over the place for a solution to this problem. I'm using SPD to create custom discussion thread .aspx pages for handling threads and replies. The reason for custom .aspx pages is so I can add new fields, choice options, and layouts to the input screen for a new item. Compared to OOTB discussion threads, this seemed to make sense. However, after much trial and error and comparing to other default OOTB threads in my site collection, I can't figure out how to have the Reply action in my custom thread pages display the proper message associated with the Reply. The required Body and Subject fields are both blank when a Reply is made. Can u help??? Thanks in Advance.
Left by DMan on Sep 08, 2010 7:04 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
what is parentid in the above example ? and how to get it?
Left by deep on Oct 14, 2010 2:47 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Does the Reply option also allows to add a reply on behalf of some other user? I would like to be able to programmatically add replies on behalf of other users as we are migrating from one forum application to sharepoint.
PLease let me know if there is a way to do this.
Left by AmitC on Jan 04, 2011 9:53 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
I used the web services to create discussion board entries with replies as described by Ram Sudama. However, when I go to SP in browser and try to reply to the items created with web services get error:

Faled to get value of the "Thread Index" Value does not fall within the expected range.

Any ideas?
Left by lafaunz on Feb 09, 2011 9:56 PM

# re: How to access SharePoint's Discussion Board using Web Services. Part 2.
Requesting Gravatar...
Hi PM lafaunz,

do you get any solution for "Failed to get value of the "Thread Index" Value does not fall within the expected range" issue..
Left by Rama Selvam on Jul 13, 2012 9:19 AM

Your comment:
 (will show your gravatar)


Copyright © Szymon Kobalczyk | Powered by: GeeksWithBlogs.net