SharePoint InfoPath Submission as Anonymous User

Creation of user submitted forms within web sites typically involves some HTML and JavaScript magic; to post data to a back end business storage or processing system.  ASP.NET relies on form HTTP POST exclusively to implement the page post back mechanism, familiar to all ASP.NET developers.  Developers manipulate form posted data using server-side code, invoked after form data is submitted to the web server.  In ASP.NET this code usually exists as code behind or code inline VB.NET or C#, using the Framework to access posted data via a dedicated request object.  So what about SharePoint?

Microsoft's answer to form submission in SharePoint is InfoPath Services.  Microsoft Office InfoPath 2007 supports web browser enabled forms within Microsoft Office SharePoint 2007 ("MOSS") Enterprise using a dedicated InfoPath engine running in MOSS – InfoPath Server.  Unfortunately, if you're using Windows SharePoint Services, or any version of MOSS below Enterprise then InfoPath Services is unavailable to you, and your left with the alternative of hosting ASP.NET forms using custom developed web parts or user controls.

InfoPath Services work well – non-developers can create forms within InfoPath 2007 on their desktop and then publish these forms to a "forms library" in MOSS.  The problem comes about when hosting InfoPath forms in Publishing Web site collections with anonymous user access turned on.  Typically, site owners like to access form submitted data in a SharePoint list, for future processing, which is a problem when a user submitting form data is not authenticated.  Any good publishing site in MOSS utilizes the lock-down feature so anonymous users cannot access back-end lists, which includes write access.  With lock-down enabled, InfoPath Server is unable to submit anonymous user form data, and usually throws up an error.   Fortunately there exists a clean solution, using web service submission, detailed below in this blog post…

Getting Started

Before we get into the specifics of implementation be sure that your version of MOSS 2007 is Enterprise and that InfoPath Services is turned on.  Within SharePoint Central Administration, under the Applications Management tab, click Configure InfoPath Forms Services under the InfoPath Forms Services section.

image

Make sure that the top two check boxes are checked to allow hosting of forms.

Within the Site Settings of the top level site collection of the SharePoint site hosting InfoPath Services, click Site Collection Features. Make sure that "Office SharePoint Server Enterprise Site Collection features" is enabled.

The Forms Library

Next, create a "Form Library" within the host site collection – I suggest hosting at the top level site collection to make life easier.  For the purpose of this blog post I created a form library called "Test Forms."  You can use the default forms library installed by SharePoint, although it's best to create your own library to prevent overwriting existing published templates in the default library.

image

The Web Service

This is the tricky part of this operation.  Rather than submitting directly to SharePoint from InfoPath we're going to submit via a custom WCF Web Service, hosted in SharePoint.  Be sure to install .NET 3.0 (ideally .NET 3.5 also) on the SharePoint server. 

Check that the WCF extensions are installed into IIS – the SVC file extension should map as follows:

image 

Create a standard class library, create the following interface definition:

   1:     [ServiceContract(Namespace = "http://contoso.net/Schemas/WCF/InfoPathService/")]
   2:      public interface IInfoPathService
   3:      {
   4:          /// <summary>
   5:          /// Upload InfoPath form data to a library in SharePoint.
   6:          /// </summary>
   7:          /// <param name="formData">Form Data.</param>
   8:          [OperationContract]
   9:          void PostInfoPathForm(string formData);
  10:      }

Create a class that implements the above interface and includes the following methods:

   1:          public void PostInfoPathForm(string formData)
   2:          {
   3:              // Use elevated privileges so anonymous users can upload through
   4:              // this service.
   5:              SPSecurity.RunWithElevatedPrivileges(delegate { UploadToDocLibrary(formData); });
   6:          }
   7:   
   8:          private static void UploadToDocLibrary(string formData)
   9:          {
  10:              try
  11:              {
  12:                  // With the form data submitted to this web service, we now
  13:                  // need to find the location for submitting list data.
  14:                  XmlDocument xmlDoc = new XmlDocument();
  15:                  xmlDoc.LoadXml(formData);
  16:                  // Get the XSN location
  17:                  XmlProcessingInstruction pi = 
  18:                      (XmlProcessingInstruction)xmlDoc.SelectSingleNode("/processing-instruction("mso-infoPathSolution")");
  19:                  if (null != pi && !String.IsNullOrEmpty(pi.Value))
  20:                  {
  21:                      Match m = Regex.Match(pi.Value, "href="(.+?)"");
  22:                      if (m.Success && m.Groups.Count > 1)
  23:                      {
  24:                          string xsnLoc = m.Groups[1].Value;
  25:                          if (!xsnLoc.StartsWith("http", StringComparison.OrdinalIgnoreCase) || !xsnLoc.ToLower().Contains("/forms/"))
  26:                              throw new Exception("XSN location is not a published InfoPath document library.");
  27:                          // Open the site and web, try to get the list.
  28:                          using (SPSite site = new SPSite(xsnLoc))
  29:                          {
  30:                              using (SPWeb web = site.OpenWeb())
  31:                              {
  32:                                  web.AllowUnsafeUpdates = true;
  33:                                  string libLoc = xsnLoc.Substring(0, xsnLoc.LastIndexOf("/Forms/"));
  34:                                  // Upload form data to the library.
  35:                                  SPFolder folder = web.GetFolder(libLoc);
  36:                                  if (null == folder)
  37:                                      throw new Exception("Cannot find the InfoPath document library root folder.");
  38:                                  UTF32Encoding encoder = new UTF32Encoding();
  39:                                  byte[] data = encoder.GetBytes(formData);
  40:                                  folder.Files.Add(String.Format("{0}/{1}.xml", libLoc, Guid.NewGuid()), data);
  41:                                  web.AllowUnsafeUpdates = false;
  42:                              }
  43:                          }
  44:                      }
  45:                  }
  46:              }
  47:              catch (Exception ex)
  48:              {
  49:                  throw new Exception("Failed to upload form data", ex);
  50:              }
  51:          }

The above code runs as a privileged user, thus getting around the anonymous user posting problem, and submits the form data to the SharePoint on the users behalf.  The code above assumes that the InfoPath forms library exists at the same location as the XSN, which typically lives in the "Forms" folder of the library.

Compile the class library and add the assembly to the GAC.

Create an SVC file, named InfoPathService.svc, as follows, replace the qualified name for the assembly in the GAC, and the service namespace.

   1:  <%@ Assembly Name="Fully qualified assembly name"%> 
   2:  <%@ ServiceHost Service="My.Namespace.InfoPathService" %>

Create a web.config file as follows:

   1:  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
   2:  <configuration>
   3:    <system.serviceModel>
   4:      <services>
   5:        <service behaviorConfiguration="InfoPathService.service1Behavior" name="My.Namespace.InfoPathService">
   6:          <endpoint binding="basicHttpBinding" contract="My.Namespace.IInfoPathService" bindingNamespace="http://contoso.net/Schemas/WCF/InfoPathService/" />
   7:        </service>
   8:      </services>
   9:      <behaviors>
  10:        <serviceBehaviors>
  11:          <behavior name="InfoPathService.service1Behavior">
  12:            <serviceMetadata httpGetEnabled="true" />
  13:            <serviceDebug includeExceptionDetailInFaults="false" />
  14:          </behavior>
  15:        </serviceBehaviors>
  16:      </behaviors>
  17:    </system.serviceModel>
  18:  </configuration>

Copy the InfoPathService.svc and web.config file to subdirectory: C:Program FilesCommon FilesMicrosoft SharedWeb Server Extensions12ISAPIMyWebServices

Confirm that the service spins up using the address of your server, e.g:

http://contoso.net/_vti_bin/MyWebServices/InfoPathWebService.svc

InfoPath

With the web service installed and functioning inside SharePoint, it's now time to create a form in InfoPath 2007, and publishing it to the SharePoint forms library.  Remember I created a form library at /Test Forms earlier.

Open InfoPath 2007 on your desktop and design a new form (I'm using a modified expense report).

image

Click the Design Checker and then change the Compatibility Settings.  Input the URL of your SharePoint site collection (not the form library).

image

Under Tools, Submit Options, change the submit options to use the web service we created, as follows:

image 

When creating the data connection, be sure to submit the entire form data to the web service as a string:

image

Publishing the Form

Publish the form using the Publish command under the File menu.  Publish to SharePoint server:

image

When prompted for the location use the URL of site that contains the for library you defined.

Publish to a document library and enable browser form completion as follows:

image

Select the form library we created earlier:

image

Finally, map the fields to columns in the SharePoint list:

image

Testing the Form

Before attempting to create a new form in the form document library (using the New command from the menu), make sure that the library is configured to open forms in the browser (form library settings, advanced settings).

Presto! The form should render in the browser:

image

Try filling out the form and submitting.  The submitted data should flow via the web service and into SharePoint.

17 thoughts on “SharePoint InfoPath Submission as Anonymous User

  1. http://

    So how do you ensure that ONLY users of your Web site can access your WCF service URL? I know your Web site uses anonymous access, but isn’t it dangerous to make the WCF service anonymous, too, especially since it automatically elevates the privileges of whoever is accessing it?

    Couldn’t an attacker potentially take down not only your service but the entire SharePoint installation with a strategy like this?

  2. http://

    Guys,

    A little help, as I am just learning to program. I can not seem to get the code to compile in a web service. Here are a couple of the errors I receive:

    1. Expected class, delegate, enum, interface, or struct
    2. Type expected
    3. A namespace does not directly contain members such as fields or methods

    Thanks,

  3. robgarrett

    Hi John,

    I’ll be honest, this particular post is a little adventurous if you’re learning to program. The reasons you’re getting errors could be attributed to your development environment, missing SharePoint DLLs etc, or something else. Without more concrete information it’s hard for me to answer your inquiry.

    R.

  4. http://

    This post leaves me half way to solve my problem. Thanks to this post I now lnow that it is possible to submit an InfoPath Form with anonymous user, but I just can’t seem to get the code up an running, posting the project would be an enormous help.

  5. http://

    When I create a Data Connection in the manner described above within ‘Submit Options’ for the Infopath form, I get the following MSSOAP errors:
    Soap error:Could not find ‘/definitions’ inside the default WSDL
    Soap error:loading of the WSDL file failed.
    Any help on whats causing this.

  6. http://

    I am trying to make Infopath work and I have two questions

    1) A class library is automatically created for any framework when installed. But I didnot understand telling to Compile class library and add the assembly to GAC.

    2) When I write the code in Visual Studio how does that links to the .svc and web.config to make anonymous access work. It means where should I place my code for effective operation? I know that .svc and we.config files should be placed in an application folder on the server.

  7. http://

    This looks like the solution that I need. I couldn’t implement it though. Could you please post a sample project?

    Thanks!

  8. http://

    I’m trying to implement your solution, but I’m stuck on the step to “confirm that the service spins up”. Here’s what I see: http://yfrog.com/06capturecfvj

    How can I peak into the cause of the error? There are no related errors in the event log or the sharepoint log files. I’ve added callStack=”true” to the site’s web.config and it had no effect.

    I’ve tried to debug in my virtualized dev environment, but no breakpoint will hit.

    Please help.

  9. David Piscopo

    Hi Rob

    Very interested in your article, but I am finding it a bit hard to read since all the picture links are missing. The page also does not render very well in Internet explorer 8 (left and right chopped off a little bit). I can get around this by using some other browser like Chrome or Opera.

    Is there any chance of fixing up the images as well as having a VS project to download?

  10. Rob Garrett

    Hi David,

    Apologies – I changed the domain on WordPress and all my images broke. The post should be good now.

    I shall upload code when I’m next on my development machine.

    R.

  11. TJ

    http://urenjoy.blogspot.com/2009/04/submit-infopath-form-to-moss-2007-form.html

    I have not tested it

    1. [WebMethod]
    2. public string Save(string strdoc)
    3. {
    4.
    5. try
    6. {
    7. string fieldName = “f1”;
    8. // Use elevated privileges so anonymous users can upload through
    9. // this service.
    10. SPSecurity.RunWithElevatedPrivileges(delegate { SubmitToSPLibrary(strdoc, fieldName); });
    11. return “”;
    12. }
    13. catch (Exception ex)
    14. {
    15. Log(ex.ToString());
    16. return “error”;
    17. }
    18.
    19. }
    20. public void Log(string str)
    21. {
    22. StreamWriter Tex = File.AppendText(“c:\Backuplog.txt”);
    23. Tex.WriteLine(DateTime.Now.ToString() + ” ” + str);
    24. Tex.Close();
    25. }
    26. private void SubmitToSPLibrary(string formData, string field)
    27. {
    28. string namespaceURI = “”;
    29. try
    30. {
    31. // With the form data submitted to this web service, we now
    32. // need to find the location for submitting list data.
    33. XmlDocument xmlDoc = new XmlDocument();
    34. xmlDoc.LoadXml(formData);
    35. //Get the namespace
    36. Match mn = Regex.Match(formData, “my=”(.+?)””);
    37. if (mn.Success && mn.Groups.Count > 1)
    38. {
    39. namespaceURI = mn.Groups[1].Value;
    40. }
    41.
    42. // Get the XSN location
    43. XmlProcessingInstruction pi =
    44. (XmlProcessingInstruction)xmlDoc.SelectSingleNode(“/processing-instruction(“mso-infoPathSolution”)”);
    45.
    46. if (null != pi && !String.IsNullOrEmpty(pi.Value))
    47. {
    48. Match m = Regex.Match(pi.Value, “href=”(.+?)””);
    49. if (m.Success && m.Groups.Count > 1)
    50. {
    51. string xsnLoc = m.Groups[1].Value;
    52. if (!xsnLoc.StartsWith(“http”, StringComparison.OrdinalIgnoreCase) || !xsnLoc.ToLower().Contains(“/forms/”))
    53.
    54. throw new Exception(“XSN location is not a published InfoPath document library.”);
    55.
    56. // Open the site and web, try to get the list.
    57. Log(“Site Address: ” + xsnLoc);
    58. using (SPSite site = new SPSite(xsnLoc))
    59. {
    60. using (SPWeb web = site.OpenWeb())
    61. {
    62. web.AllowUnsafeUpdates = true;
    63. string libLoc = xsnLoc.Substring(0, xsnLoc.LastIndexOf(“/Forms/”));
    64. Log(“Library Address: ” + libLoc);
    65.
    66. // Upload form data to the library.
    67. SPFolder folder = web.GetFolder(libLoc);
    68. if (null == folder)
    69. throw new Exception(“Cannot find the InfoPath document library root folder.”);
    70.
    71. UTF32Encoding encoder = new UTF32Encoding();
    72. byte[] data = encoder.GetBytes(formData);
    73.
    74. //Get File Name
    75. XmlNodeList list = xmlDoc.GetElementsByTagName(field, namespaceURI);
    76.
    77. string fileName = “”;
    78. foreach (XmlNode node in list)
    79. {
    80. fileName = node.InnerText;
    81. }
    82. if (fileName.Trim().Length > 0)
    83. {
    84. //Add New File OR Update Existing one.
    85. folder.Files.Add(String.Format(“{0}/{1}.xml”, libLoc, fileName), data ,true);
    86. Log(“Added Successfully.”);
    87. }
    88. else
    89. {
    90. folder.Files.Add(String.Format(“{0}/{1}.xml”, libLoc, Guid.NewGuid()), data);
    91. }
    92. web.AllowUnsafeUpdates = false;
    93. }
    94.
    95. }
    96.
    97. }
    98.
    99. }
    100.
    101. }
    102.
    103. catch (Exception ex)
    104. {
    105. throw new Exception(“Failed to upload form data”, ex);
    106. }
    107.
    108. }

Comments are closed.