Showing posts with label JScript. Show all posts
Showing posts with label JScript. Show all posts

Sunday, October 18, 2009

InfoPath Error - Updating Content Type Failed When Publishing to SharePoint

When Publishing an InfoPath Form to a SharePoint site Content Type, the process may fail when trying to Update the Site Content Type if there are a large number of fields being promoted to SharePoint columns/fields in the Content Type with an "Updating Site content Type Failed" message.

One solution for this is to create a separate list with a lookup column/field back to the list which uses the Content Type created by InfoPath. The fields which are removed from the Content Type can be added to the separate list instead.

When the InfoPath Form is submitted, rules can be configured to create or update an item in the separate list which corresponds to the form being submitted, with the additional data.

How to Solve:

1. Make a full backup the InfoPath Form Template and the SharePoint Site (Content Types, Lists, etc.)

2. Choose which fields to add to the separate list instead of promoting/submitting with the form data.

3. Remove the fields from the list of promoted fields (Tools -> Form Options -> Property Promotion - or remove the fields when you next publish the form)

Note: Enough fields need to be removed to allow the form to be published successfully, as well as consistently. At this point, if you can't get the form to publish to a Content Type without receiving an "Error updating Content Type" (or similar) message by reducing the number of promoted fields, then the problem is caused elsewhere and the solution in this article will not apply.

4. Add a CAML XML template Data Connection to store the data required to add or update an item in the separate list. See InfoPath - Update Existing SharePoint List Item Programmatically Using CAML for details.

5. Add event handlers or and a main function to populate the CAML template with the data required to create or update an item in the separate list. See InfoPath - Update Repeating Elements/Nodes in a Secondary Data Source for details.

6. Create a Submit Data Connection which uses the UpdateListItems Web Service and the CAML template (created in Step 4) to create or update an item in the separate list. See InfoPath - Update Existing SharePoint List Item Programmatically Using CAML for details.

7. Configure rules to submit the CAML template using the Data Connection created in Step 6, then submit the form using the main submit data connection when the form is submitted. See InfoPath - Update Existing SharePoint List Item Programmatically Using CAML for details.

Sunday, August 30, 2009

InfoPath: Transfer Data Between 2 Forms

Overview:
When added to an InfoPath Form template, the following JScript allows data to be transferred from one form to another programmatically using code executed by the first form. For example, this can allow a set of data from a forms data sources to be extracted and transfered into another form containing similar fields. This can also allow variations, or custom versions of a form template to be sent to specific people who may speak a different language, require an alternate layout or theme, or if the data containined in the form is confidential and needs to be submitted to a secure or different location to other forms of the same type.

Alternative Methods for Transferring data between forms (without using code)


Requirements:
- InfoPath 2007 (can also be acheived using 2003)

Process:
The step descriptions below have been commented out to allow them to be pasted into the forms code. Additional notes are in blue and have also been commented, but can be ommited if required.
There are a few ways you can get the code to run. The easiest would probably be an event handler function associated with a button on the form, as the function is generated by InfoPath when you select "Edit form code" from the "Button Properties" dialog. Other options are to add the code to the submit function, which is executed by InfoPath when the form is submitted. If using "Rules and Custom Code", a combination of rules and the example below can be used to submit both form #1 and #2.





/* -----------------------------------------------------
JScript: Transfer data between two forms
22nd Feb 2009
-------------------------------------------------------- */



//1. Set the path to the "form #2" template:

var strNewFormPath = "http://path-to-infopath-form-template";


/*
Note: If the path to the form template is a UNC path ("file:///\\filesrv06\users\john\Forms\Form2.xsn"), the backslash "\" is a special character which is used to escape other special characters, including the backslash it'self. The address above will actually be interpreted as "file:///\filesrv06usersuserFormsorm2.xsn", or similar which will result in the following error: "The following URL, path, or file name is invalid".

To ensure that all characters in the address string are interpreted correctly, the backslash "\" character needs to be escaped for it to remain in the string (see below):
"file:///\\\\filesrv06\\users\\john\\Forms\\Form2.xsn" will be interpreted as "file:///\\filesrv06\users\john\Forms\Form2.xsn" when executed.
*/




//2. Open the second form:
var objNewForm = Application.XDocuments.NewFromSolution(strNewFormPath);
/*
Note: This will result in InfoPath opening the second form from the address supplied in step 1. If you recieve a runtime error at this line in the code, check to make sure that special characters have been escaped, and that the location of the form template is accessable to all users of the form.
*/




//3. Set the name spaces for the new form:

objNewForm.DOM.setProperty("SelectionNamespaces", 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-01-29T23:32:25" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"');

/*
Note: To find the name space for a data source, go to the properties of a field in the data source, then the details tab. The name space in the details tab should be all that you need.
You will need to use the name spaces from your data sources, instead of the examples above (and in step 5).
*/




//4. Load the XML Document of "form 2":
var objExternalData = new ActiveXObject("MSXML2.DomDocument.5.0");objExternalData.validateOnParse = false;objExternalData.loadXML( objNewForm.DOM.xml );
/*
Note: This loads the XML Document of the second form's main datasource, assigning the DOM Document to a variable which is then used in step 6 to access the second form's data source from the first form.
*/




//5. Set the name space for the XML data object:

objExternalData.setProperty("SelectionNamespaces", 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-01-29T23:32:25" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution" xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/directory/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"');




//6. Update the value of fields in the main or secondary data source of "form #2" with values from "form#1":

objNewForm.DOM.selectSingleNode("/my:myFields/my:Name").text = XDocument.DOM.selectSingleNode("/my:myFields/my:Name").text;
objNewForm.DOM.selectSingleNode("/my:myFields/my:Description").text = XDocument.DOM.selectSingleNode("/my:myFields/my:Description").text;

/*
Note: The above two lines will copy the values from the Name and Description fields in Form 1 to the Name and Description fields in Form 2.
The "objNewForm.DOM.selectSingleNode..." selects the required field in the secon form, which is then assigned the value from the corresponding field in the first form: "XDocument.DOM.selectSingleNode..."
*/


Alternative Methods for transferring data between forms without using code:

If using SharePoint you can submit the data to a SharePoint list from the first form eith using a CAML query and the SharePoint lists web service or the forms main submit data source, then load the data into the second form from the SharePoint list using a secondary data connection. If not using SharePoint, this can also be achieved by submitted to an external database (Access) or even an xml file (default) which can then be used as a secondary datasource in the second form to retrieve the data.

Saturday, July 25, 2009

InfoPath - Update Repeating Elements/Nodes in a Secondary Data Source

How to update repeating elements/nodes in a secondary data source by attribute:

You may have a data source in an InfoPath form which contains repeating elements, each with the same name. When this is the case elements are usually distinguished by an attribute, commonly "name". The value for the name attribute can then be used to identify the specific element in the data source. Example below:

<?xml version="1.0" encoding="UTF-8"?>

<Batch OnError="Continue">

<Method ID="1" Cmd="Update">

<Field Name="ID"></Field>

<Field Name="field1"></Field>

<Field Name="field2"></Field>

</Method>

</Batch>


Each "Field" element above corresponds to a field/column in the SharePoint list, specified when connecting to the Lists web service.
For details and instructions for setting up InfoPath data connections to SharePoint lists using CAML, see InfoPath - Update Existing SharePoint List Item Programmatically Using CAML.
The following demonstrates how to loop through each node/element in an InfoPath data source to find and update a specific element by testing against an attribute value.
The first parameter accepted passed to the function below is the name of the Field element in the CAML template to be updated, the second is the XPATH to the field which was changed by the user. The CAML data source is iterated until the required element is found by checking if the "name" attribute matches the required field in the CAML template.

JScript Example (see below for VBScript Example):

[edit - 19-09-2009] - Declare and set xmlDataSource and UpdateXML variables when the form loads by moving the lines to above the function - see below: 

//Select all nodes from the CAML XML Data Source - Declare outside of
//updateXMLDataSouce function, otherwise it will be set every time
//the function is called, which is once for each repeating element

var xmlDataSource = XDocument.GetDOM("CAML_XMLt");
var UpdateXML = xmlDataSource.selectNodes("/Batch/Method/Field");

function updateXMLDataSource(XMLFieldName, sourceField)
{

var count;

//Loop through each element in the data source to find the
//required field in the CAML template

for (count = 0; count « UpdateXML.length; count++)
{
//Test if the name attribute of the current
//element matches the required field

if (UpdateXML[count].getAttribute("Name").localeCompare(XMLFieldName) == 0)
{
//Element Found: Update the value of current
//element to the value of souceField parameter (XPATH)
var DSNode = XDocument.DOM.selectSingleNode(sourceField);
UpdateXML[count].text = DSNode.text
}
}
}
Set up On After Change event handlers on each required InfoPath form control or field in the main data source. When a user updates a value on the form, the corresponding event handler is called, which can the call the updateXMLDataSource function (above), passing the required field name in the CAML template and the XPATH for the field which was changed. Example Below (replace CAML_FIELD_NAME with the required field name from the CAML data source):
function msoxd_my_fieldNameHere::OnAfterChange(eventObj) //Generated by InfoPath
{
// Write code here to restore the global state.

if (eventObj.IsUndoRedo)
{
// An undo or redo operation has occurred and the DOM is read-only.
return;
}

// A field change has occurred and the DOM is
// writable. Write code here to respond to the changes.

//Call the updateXMLDataSource function, passing the
//required CAML field name and the XPAT to the field which
//contains the required/updated value.
updateXMLDataSource("CAML_FIELD_NAME", "/my:myFields/my:fieldNameHere");
}

Load data into CAML data source from another data source (SharePoint List) when form loads

To help distribute processing time required to retrieve, process and submit data to data connections from and InfoPath form, the initial data can be loaded into the CAML data source when the form loads. As a result, only individual elements in the data source need to be updated when data in a form's control is changed. Once the form is submitted, no further processing is required so the CAML template can be sent via the web service data connection immediately.
For instructions and details about setting up web service data connections from InfoPath forms to SharePoint lists using CAML, see InfoPath - Update Existing SharePoint List Item Programmatically Using CAML.

[edit - 19-09-2009] - Added VBScript example below.

VBScript (VBS) Example:
The following is a VBScript example of how to loop through a repeating element in the data source (primary or secondary) of an InfoPath Form.
'Load the XML Document of the repeating data source Dim xmlDataSource Dim UpdateXML Set xmlDataSource = XDocument.GetDOM("CAML") Set UpdateXML = xmlDataSource.selectNodes("/Batch/Method/Field") 'This loops through the repepeating element in the data source and 'sets the value of the repeating field if the name attribute matches 'the first param: XMLFieldName. 'the sourceField param accepts a string containing the XPath to the 'field which has the value which is to be set as 'the value of the required repeating element. Sub updateXMLDataSource(XMLFieldName, sourceField) Dim count Dim DSNode 'Loop through each element in the data source to find the 'required field in the CAML template For count = 0 To UpdateXML.length -1 'Test if the name attribute of the current 'element matches the required field If UpdateXML(count).getAttribute("Name") = XMLFieldName Then 'Element Found: Update the value of current 'element to the value of souceField parameter (XPATH) Set DSNode = XDocument.DOM.selectSingleNode(sourceField) UpdateXML(count).text = DSNode.text End If Next End Sub
The updateXMLDataSource() function can the be called using the event handler of a button of the form of the OnAfterChange event  handler of the field which contains the value to be set to the repeating element when found by the function.
'You can then call the Sub using the event handler of a button on the form, 'or the OnAfterChange Event of a field in the main data source (below). Sub CTRL5_5_OnClick(eventObj) ' Write your code here Call updateXMLDataSource("field2", "/my:myFields/my:fieldText") End Sub '======= ' The following function handler is created by Microsoft Office InfoPath. ' Do not modify the name of the function, or the name and number of arguments. ' This function is associated with the following field or group (XPath): /my:myFields/my:fieldText ' Note: Information in this comment is not updated after the function handler is created. '======= Sub msoxd_my_fieldText_OnAfterChange(eventObj) ' Write code here to restore the global state. If eventObj.IsUndoRedo Then ' An undo or redo operation has occurred and the DOM is read-only. Exit Sub End If ' A field change has occurred and the DOM is writable.  ' Write code here to respond to the changes. Call updateXMLDataSource("field2", "/my:myFields/my:fieldText") End Sub