Tag Archives: Microsoft InfoPath

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.