Now we’ll configure the SubmitDocument data adapter.
-
We should be back at the data connections dialog box. Click Add....
-
Select the Create a new connection to radio button. Then choose Receive data. Click Next in the wizard. Note: you are choosing Receive data even though think of the operation as submitting the document. This is a result of how the web service method was defined.
-
Under From where do you want to receive your data?, choose Web service. Click Next.
-
Enter the location of the Qdabra DBXL Document web service, same as above in step 6. Click Next.
-
Select the operation called SubmitDocument, near the bottom of the list. Click Next.
-
No value is needed for any of the parameters on this panel. Just click Next.
-
There is no need to store a copy of the data in the form template. Just click Next.
-
Uncheck the box Automatically retrieve data when the form is opened. Our code will do that instead. Then click Finish.
-
Back at the data connections dialog box, click Close.
Adding the On Load and Submit handlers
In this series of steps we will hook up the On Load and Submit handlers to form code using Visual Studio Tools for Applications (VSTA). However, we won’t fill in the event handlers yet.
-
Click the Tools menu, locate the submenu Programming, and choose the item Loading Event.... VSTA will launch and show code for an empty event handler, called FormEvents_Loading.
-
Back in InfoPath Designer, go to the Tools menu and choose Submit Options....
-
In Expense Report, Allow users to submit this form will already be checked. If it is not for your form template, check it now.
-
Choose Perform custom action using Code.
-
Click Edit Code.... VSTA will flash on the task bar. If you switch to it you’ll notice another empty submit handler, called FormEvents_Submit, has appeared.
-
Back in InfoPath Designer, click OK to dismiss the submit handler dialog box.
Writing the custom code to query and submit the DBXL document
In this series of steps we will hook up the On Load and Submit handlers to form code using Visual Studio Tools for Applications (VSTA). However, we won’t fill in the event handlers yet.
-
We’ll need to add some subroutines to the form code which will handle querying the web services we added earlier.
a. If you are programming in Visual Basic, insert the following code just before the End Class statement at the bottom of the file:
Private Function GetDocTypeName() As String
' Return the document type name that will be used in DBXL to identify the documents.
' For tutorial simplicity this is hard-coded, but can be obtained via more flexible methods for production templates.
Return "MyDocType"
End Function
Private Function LoadFromDbxl(ByVal docType As String, ByVal docId As String) As Boolean
Try
Dim domGetDocument As XPathNavigator = DataSources("GetDocument").CreateNavigator()
' Set docId argument for the DBXL web service call.
domGetDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:GetDocument/tns:docId", NamespaceManager).SetValue(docId)
' Invoke the web service method to query DBXL for the document.
Dim connGetDocument As DataConnection = DataConnections("GetDocument")
connGetDocument.Execute()
' Check for error.
If Not domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:GetDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean Then
Throw New Exception("GetDocument failed")
End If
' Replace main DOM with obtained document.
Dim root As XPathNavigator = MainDataSource.CreateNavigator().SelectSingleNode("/child::*", NamespaceManager)
Dim newDoc As XPathNavigator = domGetDocument.CreateNavigator().SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:docInfo/tns:Content/node()", NamespaceManager)
root.InnerXml = newDoc.InnerXml
' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
InsertQdabraDbxlPi(docType, docId)
Catch
Return False ' Failure.
End Try
Return True ' Success.
End Function
Private Function SubmitToDbxl(ByVal docType As String, ByVal name As String, ByVal author As String, ByVal description As String) As Boolean
Dim domMainDocument As XPathNavigator = MainDataSource.CreateNavigator()
Dim domSubmitDocument As XPathNavigator = DataSources("SubmitDocument").CreateNavigator()
Try
' Set the arguments for the SubmitDocument web service call.
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:docTypeName", NamespaceManager).SetValue(docType)
' Notice that tns:xml will contain the entire main document being submitted to DBXL.
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:xml", NamespaceManager).SetValue(MainDataSource.CreateNavigator().OuterXml)
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:name", NamespaceManager).SetValue(name)
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:author", NamespaceManager).SetValue(author)
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:description", NamespaceManager).SetValue(description)
' Invoke the web service method to submit the document data to DBXL.
Dim connSubmit As DataConnection = DataConnections("SubmitDocument")
connSubmit.Execute()
' Check for error.
If Not domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:SubmitDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean Then
Throw New Exception("SubmitDocument failed")
End If
' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
Dim docId As String = domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:docId", NamespaceManager).Value
InsertQdabraDbxlPi(docType, docId)
Catch
Return False ' Failure.
End Try
Return True ' Success.
End Function
Private Sub InsertQdabraDbxlPi(ByVal docType As String, ByVal docId As String)
Dim domMainDocument As XPathNavigator = MainDataSource.CreateNavigator()
' Remove any existing QdabraDBXL PI.
Dim oldPi As XPathNavigator = domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'QdabraDBXL']", NamespaceManager)
If Not oldPi Is Nothing Then
oldPi.DeleteSelf()
End If
If docId <> "" Then
' Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
Dim newPi As String = String.Format("<?QdabraDBXL docid='{0}' doctype='{1}' ?>", docId, docType)
domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'mso-infoPathSolution']", NamespaceManager).InsertBefore(newPi)
End If
End Sub
b. If you are programming in Visual C#, use this code instead, inserted just after the closing brace for the empty FormEvents_Submit function:
public string GetDocTypeName()
{
// Return the document type name that will be used in DBXL to identify the documents.
// For tutorial simplicity this is hard-coded, but can be obtained via more flexible methods for production templates.
return "MyDocType";
}
private bool LoadFromDbxl(string docType, string docId)
{
try
{
XPathNavigator domGetDocument = DataSources["GetDocument"].CreateNavigator();
// Set docId argument for the DBXL web service call.
domGetDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:GetDocument/tns:docId", NamespaceManager).SetValue(docId);
// Invoke the web service method to query DBXL for the document.
DataConnection connGetDocument = DataConnections["GetDocument"];
connGetDocument.Execute();
// Check for error.
if (!domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:GetDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean)
throw new Exception("GetDocument failed");
// Replace main DOM with obtained document.
XPathNavigator root = MainDataSource.CreateNavigator().SelectSingleNode("/child::*", NamespaceManager);
XPathNavigator newDoc = domGetDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:GetDocumentResponse/tns:docInfo/tns:Content/node()", NamespaceManager);
root.InnerXml = newDoc.InnerXml;
// Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
InsertQdabraDbxlPi(docType, docId);
}
catch
{
return false; // Failure.
}
return true; // Success.
}
private bool SubmitToDbxl(string docType, string name, string author, string description)
{
XPathNavigator domMainDocument = MainDataSource.CreateNavigator();
XPathNavigator domSubmitDocument = DataSources["SubmitDocument"].CreateNavigator();
try
{
// Set the arguments for the SubmitDocument web service call.
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:docTypeName", NamespaceManager).SetValue(docType);
// Notice that tns:xml will contain the entire main document being submitted to DBXL.
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:xml", NamespaceManager).SetValue(MainDataSource.CreateNavigator().OuterXml);
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:name", NamespaceManager).SetValue(name);
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:author", NamespaceManager).SetValue(author);
domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:queryFields/tns:SubmitDocument/tns:description", NamespaceManager).SetValue(description);
// Invoke the web service method to submit the document data to DBXL.
DataConnection connSubmit = DataConnections["SubmitDocument"];
connSubmit.Execute();
// Check for error.
if (!domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:SubmitDocumentResult/tns:Success", NamespaceManager).ValueAsBoolean)
throw new Exception("Submit failed");
// Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
string docId = domSubmitDocument.SelectSingleNode("/dfs:myFields/dfs:dataFields/tns:SubmitDocumentResponse/tns:docId", NamespaceManager).Value;
InsertQdabraDbxlPi(docType, docId);
}
catch
{
return false; // Failure.
}
return true; // Success.
}
private void InsertQdabraDbxlPi(string docType, string docId)
{
XPathNavigator domMainDocument = MainDataSource.CreateNavigator();
// Remove any existing QdabraDBXL PI.
XPathNavigator oldPi = domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'QdabraDBXL']", NamespaceManager);
if (oldPi != null)
{
oldPi.DeleteSelf();
}
if (docId != "")
{
// Add new or updated QdabraDBXL PI so subsequent saves will overwrite DBXL document.
string newPi = String.Format("<?QdabraDBXL docid=\"{0}\" doctype=\"{1}\" ?>", docId, docType);
domMainDocument.SelectSingleNode("/processing-instruction()[local-name(.) = 'mso-infoPathSolution']", NamespaceManager).InsertBefore(newPi);
}
}
-
Before we go any further I want to point out a special function (found at the top of the just-inserted code block) called GetDocTypeName. This function will identify the document type in DBXL that documents stored under. The name here will be used when creating the DBXL document type record in the DBXL Administration Tool later. Although the document type string is hard-coded in this example, there are other ways of storing it that may be more appropriate in a production scenario. For example, you might create an xml file containing configuration information and attach it to your form template as a static xml data source. In the function GetDocTypeName you return the value of the xml node value in the data source. This provides more flexibility since the document type can be modified if needed without recompiling the form code. Other ways include having the form template use a web service data adapter to query the appropriate document type or using a query string. For the tutorial this string can remain as is.
-
Next, we will fill in the FormEvents_Loading event handler.
a. If you are programming in Visual Basic, insert the following code into the body of the FormEvents_Loading subroutine:
Me.NamespaceManager.GetNamespacesInScope(XmlNamespaceScope.All)
Dim docId As String = Nothing
' Do a case-insensitive search for the docid querystring parameter on the URL
For Each inputParameter As System.Collections.Generic.KeyValuePair(Of String, String) In e.InputParameters
If (inputParameter.Key.Equals("docid", StringComparison.OrdinalIgnoreCase)) Then
docId = inputParameter.Value
End If
Next
If Not String.IsNullOrEmpty(docId) Then
LoadFromDbxl(GetDocTypeName(), docId)
End If
b. If you are programming in Visual C#, insert the following code into the body of the FormEvents_Loading function:
this.NamespaceManager.GetNamespacesInScope(XmlNamespaceScope.All);
string docId = null;
// Do a case-insensitive search for the docid querystring parameter on the URL
foreach (System.Collections.Generic.KeyValuePair<string, string> inputParameter in e.InputParameters)
{
if (inputParameter.Key.Equals("docid", StringComparison.OrdinalIgnoreCase))
{
docId = inputParameter.Value;
}
}
if (!String.IsNullOrEmpty(docId))
{
LoadFromDbxl(GetDocTypeName(), docId);
}
-
Finally, we will fill in the code for the FormEvents_Submit handler.
a. If you are programming in Visual Basic, insert the following code into the body of the FormEvents_Submit subroutine:
' These document properties can be customized as desired
Dim title As String = "Expense Report"
Dim user As String = Application.User.UserName
Dim description As String = MainDataSource.CreateNavigator().SelectSingleNode("/my:expenseReport/my:purpose", NamespaceManager).Value
SubmitToDbxl(GetDocTypeName(), title, user, description)
e.CancelableArgs.Cancel = False
b. If you are programming in Visual C#, insert the following code into the body of the FormEvents_Submit function:
// These document properties can be customized as desired
string title = "Expense Report";
string user = Application.User.UserName;
string description = MainDataSource.CreateNavigator().SelectSingleNode("/my:expenseReport/my:purpose", NamespaceManager).Value;
SubmitToDbxl(GetDocTypeName(), title, user, description);
e.CancelableArgs.Cancel = false;
-
The code is now finished. Switch back to the InfoPath Designer.
Converting the data source connections for server use
In order for the form template to be able to access web service methods, the data connections must be converted to udcx files on a server.
-
Create a Data Connections Library on your SharePoint server.
a. Navigate in your web browser to your SharePoint web site.
b. Click on Document Center.
c. On the right side of the page, click on the Site Actions menu and choose Create.
d. Under the Libraries heading, choose Data Connection Library.
e. Enter a name, such as MyDataConnections and hit OK.
f. You will now have a new Data Connections Library at a URL like http://<your-server-name>/Docs/MyDataConnections/Forms/AllItems.aspx .
-
Convert the form template’s data connections.
a. In the InfoPath Designer, from the Tools menu, open Data Connections....
b. At this time we need to remove the Main submit data connection that came with the sample template since it will prevent deployment on the server. Make sure Main submit is selected and click Remove. Then answer Yes to the dialog.
c. Select GetDocument and click Convert.
d. In the dialog, enter the path to the udcx file you wish to create in your data connections library. This should be of the form http://<your-server-name>/Docs/MyDataConnections/GetDocument.udcx . Hit OK.
e. Select SubmitDocument and click Convert.
f. In the dialog, enter the path to the udcx file you wish to create in your data connections library. This should be of the form http://<your-server-name>/Docs/MyDataConnections/SubmitDocument.udcx . Hit OK.
g. In the data connections dialog, click Close.
-
Each udcx must be approved on the server.
a. Navigate to the data connections library, using the URL you obtained in step 32(f).
b. Click the dropdown menu that appears when hovering near GetDocument and choose Approve/reject from this menu.
c. Click the Approved option on the page that comes up. Click OK.
d. Click the dropdown menu that appears when hovering near SubmitDocument and choose Approve/reject from this menu.
e. Click the Approved option on the page that comes up. Click OK.
Publishing a deployable form template
-
In the InfoPath Designer, go to the File menu and choose Publish..., then click OK in response to the message This form template has been modified since it was last saved.
-
On the first panel of the wizard under Where do you want to publish the form template?, select To a network location. Click Next.
-
Under Form template path and file name enter a path and filename on the local hard disk. An example might be c:\ExpenseReport-Published.xsn . Click Next.
-
On the next panel, delete the path in the box such that the box is empty. Providing a path here can sometimes prevent deployment on the server. Click Next.
-
In response to the warning that starts Users will not be able to open this form..., hit OK.
-
Click Publish. Then click Close.
Deploying the form template
-
Open the SharePoint 3.0 Central Administration tool from the Programs menu on your server. This will navigate a web browser to the administration page for your server.
-
Click Applications Management.
-
Under InfoPath Forms Services, click Manage form templates.
-
Click Upload form template.
-
Enter the file path to which you published your finished template, for example c:\ExpenseReport-Published.xsn . Click Upload.
-
Click OK on the resulting success page.
-
Click the menu that appears when hovering over the form template you just added and choose Activate to a site collection.
-
Click the Site collection selector menu and choose Change Site Collection. (Note, SharePoint almost always defaults to the wrong site collection the first time a form template is activated.)
-
In the pop-up window, click the Web Application selector menu and choose Change Web Application.
-
Click SharePoint - 80, and then click the / to activate to the entire site collection. Click OK. The pop-up window will disappear.
-
Click OK.
Configuring DBXL for your document type
-
Open the DBXL Administration Tool. This can be found by navigating to the DBXL start page on your server, usually at http://<your-server-name>/QdabraWebService/default.htm and then clicking DBXL Administration Tool.
-
Click New Configuration in the tool.
-
Under Document Type Details, find the Name text box and enter the name of your document type. As you will recall, we pasted in a function called GetDocTypeName that contained a hard-coded string MyDocType. This field is how the form template connects to DBXL. Enter MyDocType in the Name box.
-
Find XSN File and click the control that says Click here to attach file. Find your finished template, for example c:\ExpenseReport-Published.xsn and attach that file.
-
Click Save.
Testing new document creation and updating
-
In the DBXL Administration Tool click the Documents tab and observe that there are no documents listed.
-
In a new web browser window enter a URL to IPFS launching your document. This URL will look something like this: http://<your-server-name>/_layouts/formserver.aspx?XsnLocation=/FormServerTemplates/ExpenseReport-Published.xsn&OpenIn=Browser .
-
In the form, fill out the required fields (manager’s email address) and enter test1 in the Business Purpose field. Click Submit.
-
In the DBXL Administration Tool click Refresh and verify that the document appears with test1 in the Description column.
-
In the form, modify Business Purpose to say test2. Click Submit.
-
In the DBXL Administration Tool click Refresh and verify that the document now has test2 in the Description column. The Doc. ID column has not changed because the form is now connected to the previously submitted document.
Testing opening and updating an existing document
-
To open an existing document, specify the DocID querystring parameter on the URL. You can identify a document ID by looking in DBXL Administration Tool under the Doc. ID column. Identify the document ID for the document submitted in step 59. Assume the document ID is 25, the URL to open this document would look like: http://<your-server-name>/_layouts/formserver.aspx?XsnLocation=/FormServerTemplates/ExpenseReport-Published.xsn&OpenIn=Browser&DocID=25 .
-
In a new web browser window, navigate to this URL, setting the DocID parameter to the one for your document. Verify that Business Purpose contains test2.
-
Modify Business Purpose to say test3. Click Submit.
-
In the DBXL Administration Tool click Refresh and verify that the document now has test3 in the Description column.
I hope you have enjoyed exploring how to connect InfoPath Forms Server with DBXL and that it opens up all kinds of new possibilities for your forms solutions!