Microsoft InfoPath 2010
The official blog of the Microsoft InfoPath team

Submitting to 'this' document library

Submitting to 'this' document library

Rate This

Have you ever needed to develop an InfoPath form template that submits back to a SharePoint document library but you did not initially know the server or library name when developing the form? Or have a scenario where your InfoPath form template could be published (or added as a content type) to multiple SharePoint document libraries and you need the submit location to be dynamic? Well read on to find out how you can do this!

When you create an InfoPath form template that needs to be able to submit to a SharePoint document library, you need to specify that connection in the initial design of the form template. However, at run time you can use managed code to determine the server and library name where the form was launched and then modify the “FolderUrl” property of the submit data connection so the form is submitted to the appropriate library.

So let’s get started setting up this sample. The C# walkthrough below uses the new InfoPath 2007 managed object model; below, you will find attached the same logic implemented in InfoPath 2003 managed object model and in InfoPath 2003 JScript.

Step 1: Create a sample InfoPath Form Template

Create a browser-compatible form template as following:

  1. Add 3 text box controls, all with a data type of text, named as follows: strFormURL, strLocation and strFolderName
  2. Add a “Submit” data connection named “Main submit”:
    • Set the document library name to a dummy SharePoint document library (i.e. http://server/dummyLib)
    • Use the concat function to concatenate the strFolderName field and _Test for the File Name property: concat(my:strFolderName, "_Test")
    • Enable the Allow overwrite if file exists property
  3. Set the Security Level of the form to Full Trust and sign the form template with a digital certificate
  4. Enable the Submit functionality (Tools | Submit Options) and choose the “Perform custom action using code” option

Now that we have the form, controls and submit functionality, let’s add the code to make this process work:

 

Step 2: Add the code

  • Click the Edit Code button on the Submit Options dialog
  • Create the following FormState Dictionary object – this will be used to store the form’s location when the form is initially opened:

private object _strUri
{
   get { return FormState["_strUri"];
  
set { FormState["_strUri"] = value; }
}

 

  • Add the following code to the Forms Loading event: 

// Get the Uri (or SaveLocation in a browser form) of where
// the form was opened.
// See if the form was opened in the browser

Boolean OpenedInBrowser = Application.Environment.IsBrowser; 

// If so, we will get the "SaveLocation" from the InputParameters

if (OpenedInBrowser)
  
_strUri = e.InputParameters["SaveLocation"].ToString();
else  

   //If it was opened in the client, we will get the Uri
  
_strUri = this.Template.Uri.ToString();

 

// Populate the fields on the form - keep in mind, this

// not necessary - this is simply to see the results

PopulateLibInfo(OpenedInBrowser);

 

  • Add the following procedure to the Forms class: 

private void PopulateLibInfo(Boolean OpenedInBrowser)

{

// Create a Navigator object for the main DOM
XPathNavigator xnDoc = this.MainDataSource.CreateNavigator();

 

// Create Navigator objects for each field

XPathNavigator xnFormURL = xnDoc.SelectSingleNode("my:myFields/my:strFormURL", this.NamespaceManager);
XPathNavigator xnLocation = xnDoc.SelectSingleNode("my:myFields/my:strLocation", this.NamespaceManager);

XPathNavigator xnFolderName = xnDoc.SelectSingleNode("my:myFields/my:strFolderName", this.NamespaceManager);

 

// Get the Uri stored in the FormState Dictionary variable

string strUri = _strUri.ToString();

 

// Create a variable to store the path (URL) to the document library

string strPath = "";

if (OpenedInBrowser == true) {

   //If we are open in the browser, the strUri value is just

   //the server name and library - so we just need to get

   //the URL without the last "/"

   strPath = strUri.Substring(0, strUri.LastIndexOf("/"));

} else {

   // Parse just the path to the document library -

   // this would return something like this:

   //  http://server/library

   strPath = strUri.Substring(0, strUri.IndexOf("Forms") - 1);
}


// Now, parse the URL to where the document library resides;
// this would return something like:

//    http://server or http://server/site
string strLoc = strPath.Substring(0, strPath.LastIndexOf("/"));


// Lastly, parse the URL to return just the document library name -

// in this case,we are looking for the last "/" character

// knowing that what comes after this is the document library name

string strFolder = strPath.Substring(strPath.LastIndexOf("/") + 1);

 

// Populate the fields on the form – we will use these

// values in the Submit process

xnFormURL.SetValue(strUri);

xnLocation.SetValue(strLoc);

xnFolderName.SetValue(strFolder);
}

 

  • Add the following code to the form’s Submit event:

// Create a Navigator object for the main DOM
XPathNavigator xnDoc = this.MainDataSource.CreateNavigator();


// Create Navigator objects for the fields we will

// use to modify the FolderUrl
XPathNavigator xnLocation = xnDoc.SelectSingleNode("my:myFields/my:strLocation", this.NamespaceManager);
XPathNavigator xnFolderName = xnDoc.SelectSingleNode("my:myFields/my:strFolderName", this.NamespaceManager);

 

// Get a reference to the submit data connection

FileSubmitConnection fc = (FileSubmitConnection)this.DataConnections["Main submit"]; 

 

// Modify the URL we want to submit to by concatenating the

// xnLocation and xnFolderName values

fc.FolderUrl = xnLocation.Value + "/" + xnFolderName.Value;

 

// Execute the submit connection

try

{

   fc.Execute();

   e.CancelableArgs.Cancel = false;

}

catch (Exception ex)

{

   e.CancelableArgs.Cancel = true;
}

 

  • Build and save the project
  • Publish and test

And that is it! You now have an InfoPath form template that will submit to whatever document library the form was opened from so you do not need to know this information when designing the template.

 

Scott Heim

Support Engineer

Attachment: submitToThisLibrary.zip
Leave a Comment
  • Please add 6 and 1 and type the answer here:
  • Post
  • Hi shirinp,

    Unfortunately I have not found a way around this...other than displaying the form in the browser instead of the client. This should allow you to capture the form library URL. If you must open it in the client and have it published as a site content type, I know of no way to capture this information.

    Scott

  • Hi Scott,

    I have tried the attached solution for Infopath 2007 C# code.

    I am getting an error: "System.ArgumentOutOfRangeException, Length cannot be less than zero."

    - in the PopulateLibInfo function at the line where you are trying to Parse the path to doc library to return something like: http://server/library.

    strPath = strUri.Substring(0, strUri.IndexOf("Forms") - 1);

    When I look at the value returning from strUri.IndexOf("Forms"), I can see it is returning "-1".

    Am I going wrong somewhere!

    Thanks in advance for any kind of help.

    zullu.

  • Hi zullu,

    Where did you publish the form and how are you trying to open it?

    Scott

  • shirinp,

    Just so you know, you are not alone regarding this problem...

    I also tried passing a parameter indicating the location, but wss doesn't allow parms to be set for a content type.  Unless Scott has an idea for this.

  • I get prompted for the file name.  How do I set this in code?

  • Hi BobC,

    You would typically use a field in the data source to store the name of the submitted form. So you can either set this using a Rule or use code similar to the following:

    XPathNavigator xnDoc = this.MainDataSource.CreateNavigator();

    XPathNavigator xnFormName = xnDoc.SelectSingleNode("/my:myFields/my:formNameField", this.NamespaceManager);

    xnFormName.SetValue("Form1");

    Scott

  • I've tried to call this.Submit() method with your sample code in my own Button Clicked event, but the Forms Server tell me that I cannot call XmlForm.Submit in a SubmitEventHandler.

    I want to do this because I need to call Rules before Submit in your way.

    Can you help me?

    Peter

  • Hi pkarpati,

    You have a couple of options:

    - Change your button to simply be a "Submit" button (don't use code)

    - Use the click event of the button to call the same code that is in the "Submit" event and remove the Submit event handler.

    NOTE: if you use the 2nd option, you will  need to remove the "e.CancelableArgs" lines.

    Scott

  • Hello Scott,

    I have a question, regarding using CODE to save the form instance to a subfolder that only an admin can see.

    USE CASE:

    ========

      Once a user enters a form (such as a survey in WSS3, through a web enabled InfoPath form), the user should not be able to re-open OR read any other entries by other people.

    We thought we could have a folder called "Submitted" in the WSS library, where the form could be saved using the "SaveLocation" QueryString property, and that this folder could only be permissioned to be viewable by "Admins".

    ISSUE:

    =====

    Even when we used SPSecurity.RunWithElevatedPrivileges(),  we get an access denied for anyone NOT AUTHORIZED to see the "Submitted" subfolder. We are unable to fgiure out why.. When logged on as admin, the save works just fine.

    ---------------

    public void FormEvents_Submit(object sender, SubmitEventArgs e)

           {

               SPSecurity.RunWithElevatedPrivileges(delegate()

               {

                   // Get a reference to the submit data connection

                   FileSubmitConnection fc = (FileSubmitConnection)this.DataConnections["Main submit"];

                   // Modify the URL we want to submit to by concatenating the

                   // xnLocation and xnFolderName values

                   fc.FolderUrl = "http://servername:3000/sites/testsite/Feedback/Submitted";

                   // Execute the submit connection

                   try

                   {

                       fc.Execute();

                       e.CancelableArgs.Cancel = false;

                   }

                   catch (Exception ex)

                   {

                       e.CancelableArgs.Cancel = true;

                   }

               });

           }

    ---------------

    NOTE:

    a) The form library is called "Feedback" with a subfolder "Submitted" which has Admin Rights only.

    b) Users instantiate a form instance through a custom link which has the SaveLocation QueryString param pointing to the (hidden) "Submitted" folder, with the desired behaviour of them not being able to see the form once submitted.

    Any help you can provide would be great.

    Thanks,

    Rajiv

  • Hi Rajiv,

    There is no built-in InfoPath functionality to limit the forms a user can see once submitted. You could create a custom SharePoint "View" that uses the "[Me]" filter to limit what documents they see when they open that library; however, this is not "security" per sé just a filter.

    Regarding the permissions not working - I believe you would be better served by asking this of the SharePoint group.

    Scott

  • I am also facing the same problem as Shirinp. In my case, i upload the form to the form server and then activate it to a particular sitecollection.

    of cNow i have a number child sites in tht sitecollection which will use this form content type, so i need to find a way to get the URL of the form library at run time. Ofcourse this post solves the problem for the forms that are opened using the browser but what to do in case forms are opened using Infopath?

    Have anybody got the solution for the problem ?

  • Also, I am able to submit the new form using this method, but how can i resubmit or save the same form when it is opened in edit mode.

  • Hi Neelesh,

    If I understand correctly you are asking how to open an existing form from a library, modify it and then re-save it using the same name - correct? If so, then when you setup the "submit" data connection make sure you enable the option: Allow overwrite if file exists."

    Scott

  • Hi Scott,

    Thanks for replying.

    Is there any wrok around for my first problem?

    Also, i need to add data (at run time) to a repeating table which has two columns, one consisting of checkbox and the other has text box.

    Can you pass on some links.....

    Also, is it okay to use Sharepoint object model directly in infopath code ?

    Neelesh

  • Hi Neelesh,

    As I have mentioned a couple of times before, there is no way that I know of to get this information.

    Regarding adding data at runtime, since this is a repeating node you will want to use code to accomplish this. There are a number of ways this can be done: append the new row as I am dong below or if you want to extract the form files you can get the actual "OuterXml" of the node you want to append and use this to append the row or if this is a client-only form you can use the "ExecuteAction" method to add a new row.

    The following sample assumes the following:

    - A secondary data connection to the SQL Server Northwind Shippers table - this connection is called Shippers

    - A Repeating Table with 3 columns: f1_ID, f2_CompanyName and f3_Phone

    - On the Loading event of the form, I populate those repeating table fields using the following code:

    public void FormEvents_Loading(object sender, LoadingEventArgs e)

           {

               //Create a NamespaceManager to use when referencing namespaces

               XmlNamespaceManager ns = this.NamespaceManager;

               //Create an XPathNavigator object for the main DOM

               XPathNavigator xnDoc = this.MainDataSource.CreateNavigator();

               //Create an XPathNavigator object for the Repeating Table node

               XPathNavigator xnRepeatingTable = xnDoc.SelectSingleNode("/my:myFields/my:group1", ns);

               //Create a DataSource object for our secondary data connection

               DataSource dsShippers = this.DataSources["Shippers"];

               //Create an XPathNavigator object for the data source

               XPathNavigator xnShippers = dsShippers.CreateNavigator();

               //Create an XPathNodeIterator object so we can enumerate all the records from the

               //secondary data connection

               XPathNodeIterator xiShippers = xnShippers.Select("/dfs:myFields/dfs:dataFields/d:Shippers", ns);

               //Loop through those records

               while (xiShippers.MoveNext())

               {

                   //Create string variables for all the fields we need to populate

                   //in the Repeating table

                   string strID = xiShippers.Current.SelectSingleNode("@ShipperID", ns).Value;

                   string strCompanyName = xiShippers.Current.SelectSingleNode("@CompanyName", ns).Value;

                   string strPhone = xiShippers.Current.SelectSingleNode("@Phone", ns).Value;

                   //Use the AppendChild method to add a new row to the repeating table and

                   //concatenate each field value appropriately

                   xnRepeatingTable.AppendChild("<my:group2><my:f1_ID>" + strID + "</my:f1_ID><my:f2_CompanyName>" + strCompanyName + "</my:f2_CompanyName><my:f3_Phone>" + strPhone + "</my:f3_Phone></my:group2>");

               }

               //Cleanup

               ns = null;

               xnDoc = null;

               xnRepeatingTable = null;

               dsShippers = null;

               xnShippers = null;

               xiShippers = null;

           }

    I hope this helps!

    Scott

Page 3 of 12 (166 items) 12345»