Update Exclusive properties[AllowMultiResponses & ShowUser] of a Survey List for SharePoint using service and CSOM C#

Survey List for SharePoint is like any other List except that its got some exclusive properties:

  • ShowUser – Indicates whether to display the user’s name along side its response. Default value is True.
  • AllowMultiResponses – Indicates whether an user can post multiple response for a Survey. Default value is False.

Now usually to update other common properties of a List, like Title, etc., we can easily rely on CSOM. However, there’s no such provision for these two properties or any other exclusive properties of any other List. Here, we’ll be focussing on the Survey List only.So when the Client Object Model fails, turn to services. The service Lists.asmx, comprises of many methods related to Lists. One of the method is UpdateList. This method has the following definition:

[SoapDocumentMethodAttribute("http://schemas.microsoft.com/sharepoint/soap/UpdateList", RequestNamespace="http://schemas.microsoft.com/sharepoint/soap/", ResponseNamespace="http://schemas.microsoft.com/sharepoint/soap/", Use=SoapBindingUse.Literal, ParameterStyle=SoapParameterStyle.Wrapped)] 
public XmlNode UpdateList (
    string listName,
    XmlNode listProperties,
    XmlNode newFields,
    XmlNode updateFields,
    XmlNode deleteFields,
    string listVersion
)

The XmlNode parameter listProperties, is the one I’ll be focusing at. This will be in the format of a

XmlNode where we can update/modify the following properties:
AllowMultiResponses TRUE to allow multiple responses to the survey.
Description A string that contains the description for the list.
Direction A string that contains LTR if the reading order is left-to-right, RTL if it is right-to-left, or None.
EnableAssignedToEmail TRUE to enable assigned-to e-mail for the issues list.
EnableAttachments TRUE to enable attachments to items in the list. Does not apply to document libraries.
EnableModeration TRUE to enable Content Approval for the list.
EnableVersioning TRUE to enable versioning for the list.
Hidden TRUE to hide the list so that it does not appear on the Documents and Lists page, Quick Launch bar, Modify Site Content page, or Add Column page as an option for lookup fields.
MultipleDataList TRUE to specify that the list in a Meeting Workspace site contains data for multiple meeting instances within the site.
Ordered TRUE to specify that the option to allow users to reorder items in the list is available on the Edit View page for the list.
ShowUser TRUE to specify that names of users are shown in the results of the survey.
Title A string that contains the title of the list.

As you can see the two exclusive properties of the List Survey, ShowUser & AllowMultiResponses are present here which is exactly what we need. The other properties can also be updated using the CSOM so I won’t be using them here.

One important thing, this method simultaneously, also creates, updates, & deletes Fields/SiteColumns for the given List. However, if you don’t want to use them (as will be the case here) you can pass an empty string as parameters.

Following is the code sample to accomplish this task. The first part demonstrates how to construct the parameter, listProperties.

Desired Format
<List Title=”List_Name” Description=”List_Description” Direction=”LTR”/>

XmlDocument xmlDoc = new System.Xml.XmlDocument();
XmlNode ndProperties = xmlDoc.CreateNode(XmlNodeType.Element, "List", "");
XmlAttribute ndTitleAttrib = (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute, "Title", "");
XmlAttribute ndDescriptionAttrib = (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute, "Description", "");
XmlAttribute ndDirectionAttrib = (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute, "Direction", "");
XmlAttribute ndMultiresponse = (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute, "AllowMultiResponses", "");
XmlAttribute ndShowUser = (XmlAttribute)xmlDoc.CreateNode(XmlNodeType.Attribute, "ShowUser", "");

ndTitleAttrib.Value = "My Survey";
ndDescriptionAttrib.Value = "Allowing multiple responses for this Survey";
ndDirectionAttrib.Value = "LTR";
ndMultiresponse.Value = "TRUE";
ndShowUser.Value = "TRUE";

ndProperties.Attributes.Append(ndTitleAttrib);
ndProperties.Attributes.Append(ndDescriptionAttrib);
ndProperties.Attributes.Append(ndDirectionAttrib);
ndProperties.Attributes.Append(ndMultiresponse);
ndProperties.Attributes.Append(ndShowUser);

So basically we're creating the following node

<
List Title="My Survey" Description="Allowing multiple responses for this Survey" Direction="LTR" AllowMultiResponses="TRUE" ShowUser="TRUE"/>

Finally, we’re going to use this parameter in the UpdateList method. The following code is a sample demonstration to call this method from the Lists.asmx service. Note that since this service is dependant on the site’s url, it will always vary from site to site. Hence we’re setting the url of the web service at runtime. One more thing, all the parameters that will be passed should be string i.e., the OuterXml property for XmlNode parameters.

string webServiceUrl = ctx.Web.Url + "/_vti_bin/Lists.asmx";

StringBuilder sbEnvelope = new StringBuilder();
sbEnvelope.Append("");
sbEnvelope.Append("");
sbEnvelope.Append(String.Format(
    "" +
        "" +
            "{0}" +
            "{1}" +
            "{2}" +
            "{3}" +
            "{4}" +
            "{5}" +
        "" +
    ""
    , id, ndProperties.OuterXml, String.Empty, String.Empty, String.Empty, version));
sbEnvelope.Append("");

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(webServiceUrl);
req.Method = "POST";
req.ContentType = "text/xml; charset=\"utf-8\"";
req.Accept = "text/xml";
req.Headers.Add("SOAPAction", "\"http://schemas.microsoft.com/sharepoint/soap/UpdateList\"");
req.UserAgent = "FrontPage";
req.UseDefaultCredentials = false;

Uri targetSite = new Uri(ctx.Web.Url);
SharePointOnlineCredentials spCredentials = (SharePointOnlineCredentials)ctx.Credentials;
string authCookieValue = spCredentials.GetAuthenticationCookie(targetSite);
req.CookieContainer = new CookieContainer();
req.CookieContainer.Add(
    new Cookie("FedAuth",
        authCookieValue.TrimStart("SPOIDCRL=".ToCharArray()),
        String.Empty,
        targetSite.Authority));

using (IO.Stream stream = req.GetRequestStream())
{
    using (IO.StreamWriter writer = new IO.StreamWriter(stream))
    {
        writer.Write(sbEnvelope.ToString());
    }
}

WebResponse response = req.GetResponse();
using (IO.Stream responseStream = response.GetResponseStream())
{
    XmlDocument xDoc = new XmlDocument ();
    xDoc.Load(responseStream);

    if (xDoc.DocumentElement != null && xDoc.DocumentElement.InnerText.Length > 0)
    {
        Debug.WriteLine(String.Concat(DateTime.Now.ToShortTimeString(), " Response of the Survey List Update: ", xDoc.DocumentElement.InnerText));
    }
}

Here’s a screen-shot of the UpdateList method. You can view your site’s service at your site’s url + “_vti_bin/Lists.asmx”

Remove/Delete an existing SharePoint File C# RPC

This one deals with the removal of a document from a SharePoint site. Though this can also be achieved using CSOM but there’s one situation where the CSOM won’t be of any help, i.e., deleting a file from the root folder of a Web(SiteCollection/SubSite). Since, the root files do not belong to any List, these files cannot be handled using standard CSOM logic. For this, I am going to rely on the RPC call to the author.dll. In my previous two posts, https://realmpksharepoint.wordpress.com/2014/04/29/upload-large-files-to-the-sharepoint-documentlibrary-using-rpc-from-a-desktop-application-c/https://realmpksharepoint.wordpress.com/2014/05/09/renamemove-an-existing-sharepoint-file-using-rpc-c/ , I have demonstrated how a document (residing in the root or anywhere else) can be uploaded, moved, or renamed using RPC.
private void RemoveDocument(string documentName)
{
    string requestUrl = this.ctx.Url + "/_vti_bin/_vti_aut/author.dll";
    string method = GetEncodedString("remove documents:15.0.0.4420");
    string serviceName = GetEncodedString(ctx.Web.ServerRelativeUrl);

    string url_List = GetEncodedString(String.Concat("[", documentName, "]"));
    rpcCallString = "method={0}&service_name={1}&url_list={2}\n";

    rpcCallString = String.Format(rpcCallString, method, serviceName, url_List).Replace("_", "%5f");

    HttpWebRequest wReq = WebRequest.Create(requestUrl) as HttpWebRequest;
    wReq.Method = "POST";
    wReq.Headers["Content"] = "application/x-vermeer-urlencoded";
    wReq.Headers["X-Vermeer-Content-Type"] = "application/x-vermeer-urlencoded";
    wReq.UserAgent = "FrontPage";
    wReq.UseDefaultCredentials = false;

    Uri targetSite = new Uri(this.ctx.Web.Url);
    SharePointOnlineCredentials spCredentials = (SharePointOnlineCredentials)this.ctx.Credentials;

    string authCookieValue = spCredentials.GetAuthenticationCookie(targetSite);
    wReq.CookieContainer = new CookieContainer();
    wReq.CookieContainer.Add(
        new Cookie("FedAuth",
            authCookieValue.TrimStart("SPOIDCRL=".ToCharArray()),
            String.Empty,
            targetSite.Authority));

    using (IO.Stream requestStream = wReq.GetRequestStream())
    {
        byte[] rpcHeader = Encoding.UTF8.GetBytes(rpcCallString);
        requestStream.Write(rpcHeader, 0, rpcHeader.Length);
        requestStream.Close();

        GetResponse(wReq);
    }
}

private string GetResponse(HttpWebRequest webRequest)
{
    string responseString = String.Empty;
    using (WebResponse webResponse = webRequest.GetResponse())
    {
        using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
        {
            responseString = reader.ReadToEnd();
            byte[] fileBuffer = Encoding.UTF8.GetBytes(responseString);
        }
    }
    
    if ((responseString.IndexOf("message=successfully") < 0) && (responseString.IndexOf("msg=Save Conflict") < 0))
    {
        throw new Exception(responseString);
    }
    return responseString;
}

Now, let’s evaluate this.

  • Here we are using method “remove documents” and, “15.0.0.4420” is server extension version.
  • Service name is server relative URL of your site.
  • documentName is the name of the document to be deleted. For ex. if the doc ToBeDeleted.aspx resides in the root folder of the site then, documentName will be ToBeDeleted.aspx.
  • For authentication, we’re using the CookieContainer of HTTPWebRequest.

Here is the example of how will you call this upload method.

RemoveDocument("ToBeDeleted.aspx");

One utility method has been used here for encoding of string. Here it is for your reference.

public string GetEncodedString(string sourceString)
{
    if (!String.IsNullOrEmpty(sourceString))
    { 
        return HttpUtility.UrlEncode(sourceString).Replace(".", "%2e").Replace("_", "%5f");
    }
    else
    {
        return sourceString;
    }
}

Rename/Move an Existing SharePoint File using RPC C#

In this post, I am going to demonstrate how to rename an existing file using RPC. The code is along the same line as this one, https://realmpksharepoint.wordpress.com/2014/04/29/upload-large-files-to-the-sharepoint-documentlibrary-using-rpc-from-a-desktop-application-c/
public void RenamePage(string oldUrl, string newUrl)
{
    string requestUrl = this.ctx.Url + "/_vti_bin/_vti_aut/author.dll";
    string method = GetEncodedString("move document:15.0.0.4420");
    
    string serviceName = GetEncodedString(this.ctx.Web.ServerRelativeUrl);
    oldUrl = GetEncodedString(oldUrl);
    newUrl = GetEncodedString(newUrl);
    string urlList = GetEncodedString("[]");
    
    rpcCallString = "method={0}&service_name={1}&oldUrl={2}&newUrl={3}&url_list={4}&rename_option=findbacklinks&put_option=edit\n";

    rpcCallString = String.Format(rpcCallString, method, serviceName, oldUrl, newUrl, urlList).Replace("_", "%5f");

    HttpWebRequest wReq = WebRequest.Create(requestUrl) as HttpWebRequest;
    wReq.Method = "POST";

    wReq.Headers["Content"] = "application/x-vermeer-rpc";
    wReq.Headers["X-Vermeer-Content-Type"] = "application/x-vermeer-rpc";

    wReq.UserAgent = "FrontPage";
    wReq.UseDefaultCredentials = false;
    
    Uri targetSite = new Uri(this.ctx.Web.Url);
    SharePointOnlineCredentials spCredentials = (SharePointOnlineCredentials)this.ctx.Credentials;
    string authCookieValue = spCredentials.GetAuthenticationCookie(targetSite);
    wReq.CookieContainer = new CookieContainer();
    wReq.CookieContainer.Add(
        new Cookie("FedAuth",
            authCookieValue.TrimStart("SPOIDCRL=".ToCharArray()),
            String.Empty,
            targetSite.Authority));

    wReq.BeginGetRequestStream(new AsyncCallback(gotRequestStream), wReq);
}

private void gotRequestStream(IAsyncResult asynchronousResult)
{
    HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
    Stream requestStream = webRequest.EndGetRequestStream(asynchronousResult);
    List<byte> uploadData = new List<byte>();
    uploadData.AddRange(Encoding.UTF8.GetBytes(rpcCallString));
    
    byte[] fileData = uploadData.ToArray();
    requestStream.Write(fileData, 0, fileData.Length);
    requestStream.Close();
    webRequest.BeginGetResponse(new AsyncCallback(gotResponse), webRequest);
}

private void gotResponse(IAsyncResult asynchronousResult)
{
    HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
    HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
    Stream responseStream = webResponse.GetResponseStream();
    StreamReader reader = new StreamReader(webResponse.GetResponseStream());
    
    string responseString = String.Empty;
    responseString = reader.ReadToEnd();
    byte[] fileBuffer = Encoding.UTF8.GetBytes(responseString);
    responseStream.Close();
    reader.Close();
    webResponse.Close();
    
    if (responseString.IndexOf("\n
message=successfully"

) < 0)
    {
        throw new Exception(responseString);
    }
}

Now, let’s evaluate this.

  • Here we are using method “move document” and, “15.0.0.4420” is server extension version.
  • Service name is server relative URL of your site.
  • oldUrl is the current url of the page and newUrl is the new url to be set.
  • For authentication, we’re using the CookieContainer of HTTPWebRequest.

Here is the example of how will you call this upload method.

RenamePage("default.aspx", "defaultNew.aspx");

Here a utility method, GetEncodedString has been used here for encoding of string. Here it is for your reference.

public string GetEncodedString(string sourceString)
{
    if (!String.IsNullOrEmpty(sourceString))
    { 
        return HttpUtility.UrlEncode(sourceString).Replace(".", "%2e").Replace("_", "%5f");
    }
    else
    {
        return sourceString;
    }
}

Check out here to get some additional info about move document, http://msdn.microsoft.com/en-us/library/ms440627%28v=office.14%29.aspx.

Get the CultureInfo of a SharePoint Online site using Client Object Model C#

CSOM code gets executed from client machine and there’s a fair amount of chance that the current machine culture will not match with that of SharePoint Online site. Now, the problem is that for a ListItem of any List, SharePoint returns DateTime value in a string format. So, if you try to convert the string to a DateTime, you’ll get a FormatException that, the String was not recognized as a valid DateTime.

So, the solution is to get the current culture of the SharePoint Online site, and then apply the same to the current thread. To do that you need to query the LocaleId of the current site which, you can then use to get the CultureInfo.

//Getting and applying the SharePoint site culture to the working thread 
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo(Convert.ToInt32(ctx.Web.RegionalSettings.LocaleId));

I have successfully tested this code for the following two conditions. My machine’s culture was en-US [MM/DD/YYYY] :-

  • en-CA [DD/MM/YYYY]
  • en-ZA [YYYY/MM/DD]

You can also see the List of CultureInfo.

Import SharePoint Site Search Configuration using Client Object Model C#

In my previous post, https://realmpksharepoint.wordpress.com/2014/03/03/export-sharepoint-site-search-configuration-using-client-object-model-c/, I had demonstrated how to export the Search Configuration of a SharePoint site.  Here, I will show how to import an exported search configuration.

To import the Search Configuration, we have to use the following namespace

using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Search.Administration; 
using Microsoft.SharePoint.Client.Search.Portability; 

In the following code, the string variable, searchConfiguration, holds the search configuration

ClientContext ctx = new ClientContext(weburl);
ctx.Credentials = new SharePointOnlineCredentials(userName, passWord);
SearchConfigurationPortability searchConfigurationPortability =

new SearchConfigurationPortability(ctx);
SearchObjectOwner searchObjectOwner = new SearchObjectOwner(ctx, SearchObjectLevel.SPWeb);

searchConfigurationPortability.ImportSearchConfiguration(searchObjectOwner, searchConfiguration);
ctx.ExecuteQuery();