
First time foray into Sharepoint
First time foray into SharePoint Web services.
We have been getting more and more clients asking us about interfacing with SharePoint but none of our clients were using it. Finally we have a client that depends on SharePoint and needed us to write an interface. With the demands on our programming pool, we always wait for demand before writing something. Now was the time. I decided to write this blog entry to help any other developers wade thru working with SharePoint Services.
I hope this information helps others who find themselves having to wade thru the SharePoint Web Services. I cannot take much credit for the information in this article without first acknowledging the fact that I scoured the web looking for samples and tidbits of information. I also hired Shervin Shabiki, from Computer Ways to help me get started. Additionally, I attended the Orlando code camp and sat thru most of the SharePoint sessions so I could get a feel for what I was in for.
Getting Started
Install SharePoint. I used WSS, (Windows SharePoint Services) a free download that runs on Windows 2003 Server. SharePoint comes with an awesome object model that makes programming to it pretty easy, but we had a few restrictions that prevented us from using the object model. We wanted to support interfacing with a hosted SharePoint site, and many of the hosted SharePoint sites do not allow access to the server, so we needed to be sure our interface would work without accessing the server by any method other than the browser interface and the web services. This restriction alone is what prevented us from using the object model. So for the purpose of this post, be aware that there are easier ways to do this, but we are only going to use the web services.
Explore
Spend some time learning how SharePoint works. Before the code camp, I had not even seen SharePoint, and was only vaguely familiar with what it actually was. I had listened to a couple of DotNetRocks podcasts about it and I was under the impression that is was a product like Dot Net Nuke. What an underestimation that was. While Dot Net Nuke and SharePoint both allow you to create a portal, I think that SharePoint is much more than that. I have heard it described in a number of ways, and when I ask people who know, what it is, I have heard a variety of descriptions. Sort of like when several eye witnesses report the same event, they all see if a bit differently. I guess that is because SharePoint is so big, that is allows you to do so much, it kind of depends on what you want it to be. I am by no means an expert, but after about a month of total immersion, I still don’t feel like I fully know what it is. So keep that in mind as you read thru this stuff.
I have played around with some of the templates and it is a pretty neat tool that can solve a lot of problems when you need people to collaborate on things, over the web. The easiest way to get started is to sign up for a hosting account and mess around with it. I used www.apps4rent.com and it cost $8.95 a month to setup an account with no contract. Within an hour or so I was using SharePoint without having to install it, or even have a Windows 2003 server.
While that worked for getting me familiar with SharePoint from a users point of view, their hosting plan did not give me access to the Admin site, so I could not fully use the Admin web services. To get around that I installed windows 2003 server on a box and then installed SharePoint. All development was done from a separate box, running windows xp and VS 2005
Dive in
There are many sites on the web that describe all of the different web services, so I won’t get into them here, I just want to show you what I learned thru this journey. Here are a couple of links that I found useful.
| MSDN Microsoft Documentation |
http://msdn.microsoft.com/en-us/library/cc752745.aspx |
| Steve Pietrek |
http://stevepietrek.com/ |
| Zac Smith |
http://www.trinkit.co.nz/blog/archive/2008/09/10/tech-ed-2008-session-content.aspx |
| John Holliday |
http://www.johnholliday.net/products.aspx |
| Article by Klaus Salchner |
http://www.csharphelp.com/archives4/archive602.html |
| Code Project |
http://www.codeproject.com/KB/SharePoint/ |
Objectives
Our interface needed to do a few things.
1. Build a Sub Site that was separate from the clients main SharePoint site
2. Build a 3 document libraries in this sub site.
3. Build a predefined list of folders in these libraries
4. Add custom columns to these libraries
5. Upload a file into these folders
6. Set the values for the custom columns on the uploaded document
5. Retrieve a list of all files in a specific folder
6. Open a file from one of these folders.
Pretty easy on the surface, but for the first entry into this is was a bit challenging. So here we go.
Before we can do anything we need to be sure that we can connect to the SharePoint server. In order to do that we need a few pieces of information. I am going to create a class called clsSharePoint and in that class I have some variables that will hold the site specific information
Public password As String = “password”
Public username As String = “Administrator”
Public domain As String = “http://2003Server/default.aspx” Public SharePointURL As String = “http://2003Server”
Public AdminURL As String = “http://2003server:46487/default.aspx”
Public AdminEmail As String = “ken@kengolding.com”
Public sitename As String = “Junxure”
Public companyFiles As String = “Company Files” Public Docs As String = “Docs”
Public AdminPort As String = “46487″
| Variable Name |
Description |
| password |
Password for the user with rights |
| username |
A user with full permissions on that SharePoint site, They will need full permissions because we will be adding subsites and other admin functions |
| domain |
The URL to the main SharePoint site. |
| SharePointURL |
The URL to the main site without the page name. |
| AdminURL |
The URL to the admin site, this can be found by starting the Central SharePoint Administrator on the server (Administrative Tools) and clicking site settings. Each SharePoint installation will probably have a different port number so you have to see what yours is. |
| AdminEmail |
Email address for the action user, only used as an argument when building a site. |
| sitename |
Name of the sub site that you want to build |
| Docs |
We build several document libraries, but in this example we are only going to build one. I gave the users the ability to call it whatever they like, by changing the variable. |
| AdminPort |
This is used when we build the URL for the web services. |
Setting up your project.
Create a new project in VS and add 6 web references.


| Web service name |
Web Service URL |
Name of Reference (You can call it whatever you want, but I called it this) |
| Admin |
_vti_adm/admin.asmx |
WSPAdmin |
| Lists |
_vti_bin/lists.asmx |
WSPLists |
| Document Workspace |
_vti_bin/DWS.asmx |
WSPDocumentWorkspace |
| Webs |
_vti_bin/Webs.asmx |
WSPWebs |
| Site Data |
_vti_bin/sitedata.asmx |
WSPSitedata |
Calling your first web service
I like to create a method called test in any project like this, just so I can know if things are working. Because we are using web references, and we plan on distributing this application, we will need to do a few things so that our web references while initially hard coded to the location of our development server, will work on our clients machines. Here is the initial test method. I have removed all of the try catch blocks to save typing and space, but I will discuss errors towards the end of this article.
Public Sub TestAdmin()
Dim WSPAdmin As WSPAdmin ‘ we are creating an object based off of our above web reference
Dim nc As New System.Net.NetworkCredential(username, password) ‘ we need to set our credentials
Dim xn As System.Xml.XmlNode = m_admin.GetLanguages() ‘ now we will just query the service for the languages
‘that returns an xml node, we will display the outerxml just to see if we were successful in connecting
MsgBox(xn.OuterXml)
End Sub
I created a form in this project to test this class, and on this form I have this code.
Dim sp and new clsSharePoint
sp.TestAdmin()
When that is called it produces this messagebox showing the outerxml that is returned in that webservice method.


Success, we asked the admin web service to let us know what languages are supported and it returned 1033 in an xml node. Most of what is returned will be in xml nodes. Now we will discuss a implementation items.
Dynamic Web Services
As you move your application from one client to another, we want to be able to dynamically change the URLs and credentials for the web services. I attacked that this way. First I made some private web services that were based on the web references that we hooked up in the previous steps. This will give us some available services and we won’t have to instantiate them for every call
Dim m_admin As New WSPAdmin.Admin
Dim m_folder As New WSPDocumentWorkspace.Dws
Dim m_list As New WSPLists.Lists
Dim m_web As New WSPWebs.Webs
Dim m_sitedata As New WSPSiteData.SiteData
I then created a function that will return the network credentials so I am only having to get credentials in one place. This will return a valid network credential
Public Function getcredentials() As System.Net.NetworkCredential
Dim nc As New System.Net.NetworkCredential(username, password)
Return nc
End Function
Now I created a function that will return a valid URL for the webserver depending on the inital setting of the variables with the server information.
Public Function getWebserviceURL(ByVal Service As String)
Select Case Service
Case “Admin”
Return SharePointURL & “:” & AdminPort & “/_vti_adm/admin.asmx”
Case “Sites”
Return SharePointURL & “:” & AdminPort & “/_vti_adm/admin.asmx”
Case “Lists”
Return SharePointURL & “/sites/” & sitename & “/_vti_bin/lists.asmx”
Case “DWS”
Return SharePointURL & “/sites/” & sitename & “/_vti_bin/DWS.asmx”
Case “Webs”
Return SharePointURL & “/sites/” & sitename & “/_vti_bin/Webs.asmx”
Case “SiteData”
Return SharePointURL & “/sites/” & sitename & “/_vti_bin/sitedata.asmx”
End Select
End Function
Now we need to create a new method that will initialize the above web services, so that we don’t have to deal with this in the remaining function. Now when you instantiate this class, it will change the URLs, Credentials and Timeouts on the 6 private web services
Public Sub New()
‘set the urls for each web service to the localized URL
m_admin.Url = getWebserviceURL(“Admin“)
m_admin.Credentials = getcredentials()
m_admin.Timeout = 60000
m_folder.Url = getWebserviceURL(“DWS“)
m_folder.Credentials = getcredentials()
m_folder.Timeout = 60000
‘set the credentials and timeout for each webservice
m_list.Credentials = getcredentials()
m_list.Url = getWebserviceURL(“Lists”)
m_list.Timeout = 60000
m_web.Credentials = getcredentials()
m_web.Url = getWebserviceURL(“Webs“)
m_web.Timeout = 60000
m_sitedata.Credentials = getcredentials()
m_sitedata.Url = getWebserviceURL(“SiteData“)
m_sitedata.Timeout = 60000
‘They are now all ready to use
End Sub
Now that we have a class that has some web services instantiated and hooked up, lets use them to do some stuff.
Get a list of Sites
This function will return a dataset that contains a list of sites on the server. It uses a helper function that is described later in the article. xmlNodeToDS that takes an xml node and converts it into a dataset.
This is handy to check if a site exists before you add it.
Public Function getSitesDS() As DataSet
Dim ds As New DataSet
Dim node As Xml.XmlNode
Try
node = m_web.GetAllSubWebCollection()
ds = xmlNodeToDs(node)
Return ds
Catch ex As Exception
Return ds
End Try
End Function
Add A new site
The documentation for this method is found here. http://msdn.microsoft.com/en-us/library/administration.admin.createsite.aspx
Here is the signature for this method.
CreateSite(Url, Title, Description, Lcid, WebTemplate, OwnerLogin, OwnerName, OwnerEmail, PortalUrl, PortalName)
We are going to Create a site with the local of English 1033 and the Standard new site of STS
Public Sub AddSite()
Try
m_admin.CreateSite(SharePointURL & “/sites/” & sitename, “Junxure SharePoint Site”, “This site is used for document management in Junxure”, 1033, “STS”, username, username, AdminEmail, “”, “”)
Catch soapex As System.Web.Services.Protocols.SoapException
If soapex.Detail.InnerText.StartsWith(“Another site already exists at”) Then
Else
msgbox (soapex.detail.innertext)
End If
End Try
End Sub
In the above code, I introduced the System.Web.Services.Protocols.SoapException. This object will allow you to see why your web service calls fail. You use the familiar try catch block, but instead of catching an exception, you catch a SoapException. The soapexception has a detail property and it has an innertext property that will tell you what went wrong.
Add a list to a site
Pretty straight forward code, just adds a list to the site, takes a name of the list, a desc and a type.
Public Sub addList(ByVal listname As String, ByVal description As String)
’101=shared documents use this link below to see the other types of lists to use
‘http://msdn.microsoft.com/en-us/library/lists.lists.addlist.aspx
Try
m_list.AddList(listname, description, 101)
Catch ex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
End Try
End Sub
Add A Folder to a list
Again pretty straight forward. The trick to this is providing the rootstring to the folder that you want to add. We want to add a folder called A to the list we added above. If the above list was docs, it would be at
http://2003server/sites/junxure/docs then I would call it like this
sp.AddFolder(“docs”,”A”)
That would create http://2003server/sites/junxure/docs
sp.addFolder(“docs/A”, “New Folder”) would create
http://2003server/sites/junxure/docs/A/New Folder
Private Sub AddFolder(ByVal rootstring As String, ByVal infolder As String)
Try
m_folder.CreateFolder(rootstring & “/” & infolder)
Catch soapex As System.Web.Services.Protocols.SoapException
MsgBox(soapex.Detail.InnerText)
End Try
End Sub
Retrieve all the lists in a site.
This function will return an xml node that represents all of the lists on a site. I have written it so that it will return a dataset of all of the lists on the site. It uses a helper function, that takes an xml node and returns a dataset with the nodes contents.
Public Function getListSDS() As DataSet
Dim ds As New DataSet
Dim node As Xml.XmlNode
Try
node = m_list.GetListCollection()
ds = xmlNodeToDs(node)
Return ds
Catch ex As Exception
MsgBox(soapex.Detail.InnerText)
Return ds
End Try
End Function
Here is the helper function It takes the xml node returned from many of the web service methods, and converts it into a dataset. The one trick that you may notice is that I prepend the xml from the node with <?xml version=”1.0″ encoding=’UTF-8′ ?> . Before I prepended it, I would only get errors trying to read the xml into the record set. This function is really handy if you want to see what is in the nodes that are returned.
Public Function xmlNodeToDs(ByVal node As Xml.XmlNode) As DataSet
Dim ds As New DataSet
Try
Dim result As New System.IO.MemoryStream
Dim sw As New StreamWriter(result, System.Text.Encoding.ASCII)
Dim xml As String = “”
xml = “<?xml version=’1.0′ encoding=’UTF-8′ ?>” & node.OuterXml.ToString
sw.Write(xml)
sw.Flush()
result.Seek(0, SeekOrigin.Begin)
ds.ReadXml(result, XmlReadMode.Auto)
Return ds
Catch ex As Exception
MsgBox(soapex.Detail.InnerText)
Return ds
End Try
End Function
Get all items in a list (Recursively)
If there are folders in a list, the standard calls do not work for getting the items in the list. You will have to use some CAML to query recursively. in this example we are going to query the list that is passed in and we are going to get a list of all of the items in that list, even if they are in subfolders in the list. It will be recursive as deep as the list is. This is written to return a datatable.
It uses the method GetListItems
GetListItems(listName, viewName, query, viewFields, rowLimit, queryOptions, webID)
Listname the name of the lsit
Viewname is the name of the view and an empty string will use the default view for that list. Any fields that are not in the view will not be returned.
query – this is an xml node that defines the query for the method call. In our case, we set it up by creating an xmlDoc and then using the doc to create an xmlNode. We then set an element of the node to “Query”
Viewfields is optional and we are just passing in an xmlNode that is equal to nothing
nodeQueryOptions is where you tell it what folder and what attributes to search on. Again we use the xmlDoc to create a queryoptions node, and then from that we set the innerxml to the folder that we want to query by using <Folder></Folder> that will search from the root folder. If we want to specify a folder, then we would put the folder path inbetween the <folder> tags like this <folder>FOLDERNAME\SUBFOLDERNAME</Folder>. That entry would search for all items in the Subfoldername folder
We can then put some attributes on the Node. The trick in this one is to use the <ViewAttributes Scope=”recursive”/> With this attribute set, it will search recursively thru the whole list.
When we take the resulting node, and use our helper function to put it into a dataset, we can inspect the dataset and see that it is the second table that contains the items for the list, so we return the second table. ds.tables(1)
Public Function getListItems(ByVal Listname As String) As DataTable
‘this is a recursive search for all items in the list
Dim ds As New DataSet
Try
Dim innerxml As String
Dim nodeView As Xml.XmlNode = Nothing
Dim nodeResult As Xml.XmlNode
Dim xmldoc As New System.Xml.XmlDocument()
Dim nodeQuery As XmlNode = xmldoc.CreateNode(XmlNodeType.Element, “Query”, “”)
Dim nodequeryOptions As XmlNode = xmldoc.CreateNode(XmlNodeType.Element, “QueryOptions”, “”)
innerxml = “<Folder></Folder><ViewAttributes Scope=” & Chr(34) & “Recursive” & Chr(34) & “/>”
nodequeryOptions.InnerXml = innerxml
nodeResult = m_list.GetListItems(Listname, “”, nodeQuery, nodeView, String.Empty, nodequeryOptions, “”)
ds = xmlNodeToDs(nodeResult)
If ds.Tables.Count = 1 Then
Dim dt As New DataTable
Return dt
Else
Return (ds.Tables(1))
End If
Catch soapex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
Dim dt As New DataTable
dt.TableName = “No Data”
ds.Tables.Add(dt)
End Try
End Function
Below is a partial screen shot of the above datatable loaded into a grid. You can see the field names and the contents of the xmlnode

Get the info for an item given a listname and a URL
Using this function, you can retrieve just the information for one item, rather than all the items in a list. We will query the list based on the ows_FileRef that is the URL for the items.
If you have a document with a URL of http://2003Server/sites/Junxure/Company Files/A/Anderson-Allen-1187/Financial Plan/Add.bmp you can all this function with the name of the list and this URL and you will receive a datatable with one row, containing all of the information for that one item. we use the same code as above, but we are going to change the query node to include a where clause, and that where clause will specify that we are looking for the FieldRef field to be equal to the passed in search URL.
Public Function getListItemsRecursiveByURL(ByVal Listname As String, ByVal fileURL As String) As DataTable
‘this is a recursive search for an item in the list with that URL
Dim searchURL As String = fileURL
Dim ds As New DataSet
Dim dt As New DataTable
Try
searchURL = URLDeEncode(searchURL)
searchURL = searchURL.ToLower.Replace(SharePointURL.ToLower & “/”, “”)
Dim innerxml As String = “”
Dim nodeView As Xml.XmlNode
Dim nodeResult As Xml.XmlNode
Dim xmldoc As New System.Xml.XmlDocument()
Dim nodeQuery As XmlNode = xmldoc.CreateNode(XmlNodeType.Element, “Query”, “”)
innerxml = “<Where>”
innerxml += “<Eq>”
innerxml += “<FieldRef Name=’FileRef’/>”
innerxml += “<Value Type=’Text’>” & searchURL & “</Value>”
innerxml += “</Eq>”
innerxml += “</Where>”
nodeQuery.InnerXml = innerxml
Dim nodequeryOptions As XmlNode = xmldoc.CreateNode(XmlNodeType.Element, “QueryOptions”, “”)
innerxml = “<Folder></Folder><ViewAttributes Scope=” & Chr(34) & “Recursive” & Chr(34) & “/>”
nodequeryOptions.InnerXml = innerxml
nodeResult = m_list.GetListItems(Listname, “”, nodeQuery, nodeView, String.Empty, nodequeryOptions, “”)
ds = xmlNodeToDs(nodeResult)
If ds.Tables.Count > 1 Then
Return (ds.Tables(1))
Else
Return dt
End If
Catch soapex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
Return dt
End Try
End Function
The trick to the above code is getting node query xml entered properly. If you look above you will see that we use a helper function to URLDeEncode the url that is passed in. We also have an URLencode fuction. These simply take URLS and make them safe to pass around, adding or replacing unsafe characters. Here are the two function
Public Function URLEncode(ByVal inUrl As String) As String
Try
‘reserved
inUrl = inUrl.Replace(“%”, “%25″)
inUrl = inUrl.Replace(“/”, “%2f”)
inUrl = inUrl.Replace(” “, “%20″)
inUrl = inUrl.Replace(“$”, “%24″)
inUrl = inUrl.Replace(“&”, “%26″)
inUrl = inUrl.Replace(“+”, “%2B”)
inUrl = inUrl.Replace(“,”, “%2C”)
inUrl = inUrl.Replace(“:”, “%3A”)
inUrl = inUrl.Replace(“;”, “%3B”)
inUrl = inUrl.Replace(“=”, “%3D”)
inUrl = inUrl.Replace(“?”, “%3F”)
inUrl = inUrl.Replace(“@”, “%40″)
inUrl = inUrl.Replace(Chr(34), “%22″)
inUrl = inUrl.Replace(“<”, “%3C”)
inUrl = inUrl.Replace(“>”, “%3E”)
inUrl = inUrl.Replace(“#”, “%23″)
inUrl = inUrl.Replace(“{“, “%7B”)
inUrl = inUrl.Replace(“}”, “%7D”)
inUrl = inUrl.Replace(“\”, “%5C”)
inUrl = inUrl.Replace(“|”, “%7C”)
inUrl = inUrl.Replace(“^”, “%5E”)
inUrl = inUrl.Replace(“~”, “%7E”)
inUrl = inUrl.Replace(“[", "%5B")
inUrl = inUrl.Replace("]“, “%5D”)
inUrl = inUrl.Replace(“`”, “%60″)
Return inUrl
Catch ex As Exception
msgbox (ex.message)
Return inUrl
End Try
End Function
Public Function URLDeEncode(ByVal inUrl As String) As String
Try
‘reserved
inUrl = inUrl.Replace(“%25″, “%”)
inUrl = inUrl.Replace(“%2f”, “/”)
inUrl = inUrl.Replace(“%20″, ” “)
inUrl = inUrl.Replace(“%24″, “$”)
inUrl = inUrl.Replace(“&”, “%26″)
inUrl = inUrl.Replace(“%2B”, “+”)
inUrl = inUrl.Replace(“%2C”, “,”)
inUrl = inUrl.Replace(“%3A”, “:”)
inUrl = inUrl.Replace(“%3B”, “;”)
inUrl = inUrl.Replace(“%3D”, “=”)
inUrl = inUrl.Replace(“%3F”, “?”)
inUrl = inUrl.Replace(“%40″, “@”)
inUrl = inUrl.Replace(“%22″, Chr(34))
inUrl = inUrl.Replace(“%3C”, “<”)
inUrl = inUrl.Replace(“%3E”, “>”)
inUrl = inUrl.Replace(“%23″, “#”)
inUrl = inUrl.Replace(“%7B”, “{“)
inUrl = inUrl.Replace(“%7D”, “}”)
inUrl = inUrl.Replace(“%5C”, “\”)
inUrl = inUrl.Replace(“%7C”, “|”)
inUrl = inUrl.Replace(“%5E”, “^”)
inUrl = inUrl.Replace(“%7E”, “~”)
inUrl = inUrl.Replace(“%5B”, “[")
inUrl = inUrl.Replace("%5D", "]“)
inUrl = inUrl.Replace(“%60″, “`”)
Return inUrl
Catch ex As Exception
msgbox (ex.message)
Return inUrl
End Try
End Function
Building the Query
There is a great tool out there called U2UCamlCreator.exe and with this tool you can create and execute CAML queries and see how they work.
Here are a couple of links that discuss that tool
http://www.u2u.info/Blogs/Patrick/Lists/Posts/Post.aspx?ID=1252
http://www.u2u.be/res/Tools/CamlQueryBuilder.aspx
This tool was very useful for helping figure out the required CAML queries.
Add A field to a list
If you go to the SharePoint site via a browser, you can see that it is very easy to add columns and set their properties. We need to do this programatically. I wrote a couple of functions to accomplish this.
Before I add a field, I want to see if that field already exists. I pass in a listname and a fieldname, and return true or false depending if that field exists. This function uses some classes in the Site Data Web services.
Public Function FieldExists(ByVal listname As String, ByVal Fieldname As String) As Boolean
Dim exists As Boolean = False
Try
Dim lstMetaData As New WSPSiteData._sListMetadata
Dim lstFields() As WSPSiteData._sProperty
m_sitedata.GetList(listname, lstMetaData, lstFields)
Dim field As WSPSiteData._sProperty
For Each field In lstFields
If Fieldname.ToLower = field.Title.ToLower Then
exists = True
Exit For
End If
Next field
Return exists
Catch soapex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
Return exists
End Try
End Function
This function is the fuction that adds the field to the list. It takes a listname, a fieldname, a datatype and a description. If the field is already there, it exits otherwise it adds the field to the list.
Public Sub AddField(ByVal listname As String, ByVal fieldname As String, ByVal datatype As String, ByVal description As String)
If FieldExists(listname, fieldname) Then Exit Sub
Dim innerxml As String = “”
Dim ndList As XmlNode = m_list.GetList(listname)
Dim ndVersion As XmlNode = ndList.Attributes(“Version“)
Dim xmlDoc = New System.Xml.XmlDocument()
Dim ndDeleteFields As XmlNode = Nothing
Dim ndProperties As XmlNode = xmlDoc.CreateNode(XmlNodeType.Element, “List”, “”)
Dim ndTitleAttrib As XmlAttribute = Nothing ‘ CType(xmlDoc.CreateNode(XmlNodeType.Attribute, “Title”, “”), XmlAttribute)
Dim ndDescriptionAttrib As XmlAttribute = Nothing ‘CType(xmlDoc.CreateNode(XmlNodeType.Attribute, “Description”, “”), XmlAttribute)
Dim ndNewFields As XmlNode = xmlDoc.CreateNode(XmlNodeType.Element, “Fields”, “”)
Dim ndUpdateFields As XmlNode = Nothing ‘xmlDoc.CreateNode(XmlNodeType.Element, “Fields”, “”)
innerxml = “<Method ID=’1′>”
Select Case datatype
Case “DateTime”
innerxml += “<Field Type=’DateTime’ DateOnly=’TRUE’ DisplayName=’” & fieldname & “‘ FromBaseType=’TRUE’/>”
Case “Text”
innerxml += “<Field Type=’Text’ DisplayName=’” & fieldname & “‘ Required=’FALSE’ FromBaseType=’TRUE’ Description=’” & description & “‘/>”
Case “Number”
innerxml += “<Field Type=’Number’ Name=’” & fieldname & “‘ DisplayName=’” & fieldname & “‘ Required=’FALSE’ FromBaseType=’TRUE’ Description=’” & description & “‘/>”
End Select
innerxml += “</Method>”
ndNewFields.InnerXml = innerxml
Try
‘code to actually add the field
Dim ndReturn As XmlNode = m_list.UpdateList(listname, ndProperties, ndNewFields, ndUpdateFields, ndDeleteFields, ndVersion.Value)
Catch soapex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
End Try
End Sub
This is a function that will get a datatable that contains all of the fields in a list
Public Function GetFieldsDT(ByVal listname As String) As DataTable
Dim dt As New DataTable
dt.Columns.Add(“FieldTitle“)
dt.Columns.Add(“FieldName“)
dt.Columns.Add(“DataType“)
Try
Dim lstMetaData As New WSPSiteData._sListMetadata
Dim lstFields() As WSPSiteData._sProperty
m_sitedata.GetList(listname, lstMetaData, lstFields)
Dim msg As String = lstMetaData.Title + ” :: “ + lstMetaData.DefaultViewUrl + ControlChars.Lf
Dim field As WSPSiteData._sProperty
For Each field In lstFields
dt.Rows.Add(field.Title, field.Name, field.Type)
Next field
Return dt
Catch soapex As System.Web.Services.Protocols.SoapException
msgbox (soapex.detail.innertext)
Return dt
End Try
End Function
Update a field for a specific item in a list.
You will pass in the listname, the URL of the item, the fieldname and the value to set it to.
Public Sub updateItem(ByVal Listname As String, ByVal fileURL As String, ByVal Fieldname As String, ByVal fieldValue As String)
Try
‘we need to get the ID of the item
Dim itemID As String
Dim ds1 As New DataSet
Dim dtItem As New DataTable
dtItem = getListItemsRecursiveByURL(Listname, fileURL)
If dtItem.Rows.Count = 0 Then
Exit Sub
Else
itemID = dtItem.Rows(0)(“ows_ID”)
End If
‘now we Get Name attribute values (GUIDs) for list and view.
Dim ndListView As System.Xml.XmlNode = m_list.GetListAndView(Listname, “”)
‘Create an XmlDocument object and construct a Batch element and its ‘attributes. Note that an empty ViewName parameter causes the method ‘to use the default view.
Dim doc As New System.Xml.XmlDocument()
Dim batchElement As System.Xml.XmlElement = doc.CreateElement(“Batch“)
batchElement.SetAttribute(“OnError“, “Continue”)
batchElement.SetAttribute(“ListVersion“, “1″)
batchElement.SetAttribute(“RootFolder“, Listname)
Dim innerxml As String = “”
‘You will see similar code to this all over, but not too many explanations. Here is where you pass the method in.
‘you can actually batch as many methods as you want together, and each method will have it’s own ID, starting with 1 and incrementing up
‘as you add addtional methods. In this case we are only adding one method.
‘see the notes below for information on this xml
innerxml = “<Method ID=’1′ Cmd=’Update’>”
innerxml += “<Field Name=’ID’>” & itemID & “</Field>”
innerxml += “<Field Name=’FileRef’>” & URLDeEncode(fileURL) & “</Field> “
innerxml += “<Field Name=’” & Fieldname & “‘>” & fieldValue & “</Field>”
innerxml += “</Method>”
batchElement.InnerXml = innerxml
Dim resultNode As XmlNode
resultNode = m_list.UpdateListItems(Listname, batchElement)
ds1 = xmlNodeToDs(resultNode)
Catch ex As System.Web.Services.Protocols.SoapException
SPpex(ex, “WSSupdate”)
End Try
End Sub
////////////// NOTE ON XML////////////////////////////////////////
innerxml = “<Method ID=’1′ Cmd=’Update’>”
innerxml += “<Field Name=’ID’>” & itemID & “</Field>”
innerxml += “<Field Name=’FileRef’>” & URLDeEncode(fileURL) & “</Field> “
innerxml += “<Field Name=’” & Fieldname & “‘>” & fieldValue & “</Field>”
innerxml += “</Method>”
batchElement.InnerXml = innerxml
While you will see this all over, it was a bear to make it work. You pass in the method ID as described above, it just increments for each command that you want to execute.
You then pass in the FieldName and that is the ItemID that we got from the ows_ID in the top part of this function
You also need to pass in the DEEncoded URL for the item
Then you pass in the value that you want to update it to. If you do not pass in the DeEncoded URL it will not update the value.
///////////////////////////////////////////////////////////////////////////////////////
Finally we upload a Document
After searching all over the place I could not find out how to upload a document into a list using the web services. In talking with Shervin Shabiki, he told me that you cannot upload a document using the web services. You must use another method. Here is what I came up with, borrowing from this post on CodeProject and adding some of my own ideas.
In using this function on my site, we have already added the appropriate lists, and each list has some custom columns added to them. I will pass in the clientname and since we added the clientname to our list in our custom program, it will update the clientname column with the passed in value
Public Function UploadDocument( ByVal localFile As String, ByVal remoteFile As String, ByVal Listname As String, ByVal folder As String, Optional ByVal Clientname As String = “”) As String
‘// Read in the local file
Dim sRemoteFileURL As String = “”
Dim attemptNumber As Integer = 0
Dim maxAttempts As Integer = 5
Try
Dim r As Byte()
Dim sDocLib As String = “”
Dim Strm As System.IO.FileStream = New System.IO.FileStream(localFile, System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim reader As System.IO.BinaryReader = New System.IO.BinaryReader(Strm)
Dim filecontents As Byte() = reader.ReadBytes(CInt(Strm.Length))
reader.Close()
Strm.Close()
Dim sSPURL As String = SharePointURL & “/sites/” & sitename
sDocLib = Listname & “/” & folder
Dim NC As System.Net.NetworkCredential = New System.Net.NetworkCredential(username, password, domain)
sRemoteFileURL = sSPURL & “/” & sDocLib & “/” & Trim(LTrim(RTrim(remoteFile)))
sRemoteFileURL = Replace(sRemoteFileURL, ” “, “%20″)
sRemoteFileURL = Replace(sRemoteFileURL, “\”, “/”)
Dim m_WC As WebClient = New WebClient
m_WC.Credentials = NC
Dim dtcheck As New DataTable
Do Until dtcheck.Rows.Count > 0
attemptNumber = attemptNumber + 1
‘we want to try the upload for as many times as we specified in maxAttempts variable.
r = m_WC.UploadData(sRemoteFileURL, “PUT”, filecontents)
‘After we attempt to upload it, we then use our getListItems function to see if the item made it up there, if not we try again.
dtcheck = getListItemsRecursiveByURL(Listname, sRemoteFileURL)
‘if the datatable dtcheck has rows, then the item was uploaded successfully
‘Here we have some custom code that will process a failure if the attemp number is = the max attempts meaning that we did not get the file uploaded so we
‘need to log it and move on. I do not show the code for logging the failure as it does not pertain to this post.
If attemptNumber = maxAttempts And dtcheck.Rows.Count = 0 Then
MsgBox(“Error Loading file into SharePoint” & vbCrLf & “The files as been placed into the Failed Bin”)
processfailure(localFile, remoteFile, Listname, folder, clientname)
Return localFile
End If
Loop
‘We use the update item code we wrote earlier to update the clientname field with the passed in clientname
If clientname <> “” Then updateItem(Listname, sRemoteFileURL, “ClientName”, Clientname)
Return sRemoteFileURL
Catch ex As Exception
msgbox (ex.message)
Return “Failed”
End Try
End Function
Hopefully these functions will help others as then embark on the SharePoint webservices programming tour.
If you would like me to post the code to this post, please leave a comment.