Extending the Save Functionality in InfoPath 2003 

 

David Gerhardt
3Sharp

October 2004

Applies to:
    Microsoft Office InfoPath 2003

Summary: Learn to extend the Save and Save As functionality programmatically in Microsoft Office InfoPath 2003 Service Pack 1. Examine situations where you might use extended save functionality, look at the security risks associated with locally saved forms, and identify measures you can take to help ensure form data integrity. (10 printed pages)

Contents

Introduction
Extending Save Functionality with Form Options
Extending Save Functionality with the OnSaveRequest Function
Addressing Security Issues with Locally Saved Forms
Conclusion

Introduction

With Microsoft Office InfoPath 2003 Service Pack (SP) 1, you can extend the Save and Save As functionality programmatically. InfoPath SP 1 provides support for save extensibility in the following areas:

  • Form options. These options for the form template are toggles that allow you to enable or disable certain save functionality.
  • The OnSaveRequest function. This callback function allows InfoPath to perform programmatic operations during a Save or Save As call.

By examining these areas, you can identify situations where you can take advantage of extended save functionality. You can look at the security risks associated with locally saved forms and identify measures you can take to help ensure form data integrity.

Extending Save Functionality with Form Options

You can use form options to enable or disable certain save functionality in the form. Check box options you select during form design allow users access to functionality such as Save, Save As, and Autosave by way of menus, toolbars, and keyboard shortcuts.

To enable or disable save functionality

  1. In the design mode of InfoPath, on the Tools menu, click Form Options.
  2. Click Open and Save.
  3. In the Enable Features section, select the options appropriate for your form.

In the Save behavior section, you have a check box option called Save using custom code. If you select this option and click Edit, you create the OnSaveRequest callback function in your form code.

Extending Save Functionality with the OnSaveRequest Function

OnSaveRequest is a callback function you create in your form code through the Form Options dialog box. Enabling this function allows you to override the default Save and Save As functionality and gain programmatic access to the form's dirtiness bit.

In the following sections, you can explore how to use the SaveEvent object, how to assume control of the Save As dialog box, and how to gain access to form dirtiness.

Using the SaveEvent Object

The SaveEvent object is a parameter passed into the OnSaveRequest function when the user initiates a save action. This object is limited to the scope of the OnSaveRequest function; it cannot be set as the value of a global variable within the form code. Do not confuse this object with the thisXDocument.Save method, which cannot be called in the OnSaveRequest function.

The following scenario explains the properties and methods of the SaveEvent object in the context of an expense report. In this scenario, employees at a consulting company need to record expenses incurred while at client sites. The employees can save the expense report form to the hard disk of a laptop while disconnected from the company network. Once the employee reestablishes a network connection, she can submit the form to a Microsoft BizTalk Server 2004 orchestration, which is used to determine the approval routing logic.

Distinguishing Between Save and Save As Calls

You can enable Save and Save As functionality with a single option in the Enable Features section of the Form Options dialog box, as described previously in Extending Save Functionality with Form Options. Our expense report scenario, however, requires that Save As functionality be disabled unless the form is new.

InfoPath 2003 SP 1 provides the IsSaveAs property within the OnSaveRequest function, and you can use this property to create a solution for the expense report scenario. The IsSaveAs property returns true if the event came from a Save As call, from a Save call for new forms, or from any call that originated from a read-only form. In all other cases, this property returns false. The following example uses Microsoft C# code to show how you can enable Save As functionality for new forms only.

public void OnSaveRequest(SaveEvent e)
{
    if (thisXDocument.IsNew == true || e.IsSaveAs == false)
    {
        e.IsCancelled = e.PerformSaveOperation();
        e.ReturnStatus = true;
    }
    else
    {
        thisXDocument.UI.Alert("The 'Save As' functionality
            is disabled for forms that are not new.");
    }
}

Note that even though you disable the Save As option for forms that are not new, the option still appears on the form's File menu.

Using the ReturnStatus Property

You can use the ReturnStatus property of the SaveEvent object to indicate the save event return status. This Boolean property always returns a value for the OnSaveRequest function. You must explicitly set this property value to true to return a successful save event. Otherwise, the function returns the property's initial value of false, indicating that an error was detected. In these cases, when the OnSaveRequest function completes, an error is thrown, and the user sees the message shown in Figure 1.

Click here to see larger image

Figure 1. Error message shown when the ReturnStatus property returns false (click picture to see larger image)

Using the IsCancelled Property

The IsCancelled property of the SaveEvent object works in conjunction with the ReturnStatus property to determine whether a Save As operation has been canceled. As with the ReturnStatus property, the Boolean IsCancelled property always returns a value. This property is initially set to false in the OnSaveRequest function. If a user cancels a Save As operation, then this property is set to true. Table 1 shows form behavior for the possible combinations of IsCancelled and ReturnStatus property values.

Table 1. Form Behavior with the IsCancelled and ReturnStatus Property Values

  IsCancelled = true IsCancelled = false
ReturnStatus = true The form is not saved, but the OnSaveRequest function is successful. The form is saved, and the OnSaveRequest function is successful.
ReturnStatus = false The form is not saved, an error is thrown, and an error message appears to the user (Figure 1). The form is not saved, an error is thrown, and an error message appears to the user (Figure 1).

Performing Save Operations

You can use the PerformSaveOperation method of the SaveEvent object to direct InfoPath to execute an internal save operation for a form. Configuring InfoPath to call this method before setting the ReturnStatus property to true ensures the correct form behavior, as indicated in Table 1.

Assuming Control of the Save As Dialog Box

With InfoPath 2003 SP 1, you can specify default values in the SaveAs dialog box. You can use the SetSaveAsDialogLocation and SetSaveAsDialogFileName methods to set the default location and file name, respectively, for the form.

Expanding on our expense report scenario, Save As operations use a default location of C:\Temp\ and a default file name that concatenates the employee's user name, the project name, and the report start date. This scenario assumes that the form has userPrincipalName, project, and startDate elements for capturing the default file name information. The following example uses C# code to set the default location and file name according to this scenario.

public void OnSaveRequest(SaveEvent e)
{
    string userPrincipalName = thisXDocument.DOM.selectSingleNode
        ("//my:employee/my:userPrincipalName").text;
    string projectName = thisXDocument.DOM.selectSingleNode
        ("//my:project").text;
    string startDate = thisXDocument.DOM.selectSingleNode
        ("//my:startDate").text;
    if (thisXDocument.IsNew == true || e.IsSaveAs == false)
    {
        if (e.IsSaveAs == true)
        {
            thisXDocument.UI.SetSaveAsDialogLocation("c:\\temp");
            thisXDocument.UI.SetSaveAsDialogFileName(userPrincipalName
                 + "_" + projectName + "_" + startDate);
        }
        e.IsCancelled = e.PerformSaveOperation();
        e.ReturnStatus = true;
    }
    else
    {
        thisXDocument.UI.Alert("The 'Save As' functionality
            is disabled for forms that are not new.");
    }
}

Note that the .xml file extension does not have to be added to the default file name, because handling for this extension occurs in the Save As dialog box.

By assuming control of the Save As dialog box, you can encourage users to save to a particular location with a certain file-naming convention. The SetSaveAsDialogLocation method allows you to specify a default local folder to which users can save forms. You can provide a default file name by using the SetSaveAsDialogFileName method. Users can still choose a different folder or file name in the Save As dialog box.

Gaining Access to Form Dirtiness

The SetDirty method and the IsDirty property in InfoPath 2003 SP 1 give you access to a form's dirtiness bit. The SetDirty method is especially useful when you want to use custom save handling instead of the PerformSaveOperation method or if changes to the form data can be ignored. A true or false value for the IsDirty property indicates that the form has or has not been modified since its last successful save operation, respectively. You have write access to a form's dirtiness bit when you use the SetDirty method, as shown in the following C# code example.

public void buttonClear_OnClick(DocActionEvent e)
{
    if (thisXDocument.IsNew == true)
    {
        clearFields();
        thisXDocument.SetDirty(false);
    }
}

In this example, a Clear Fields button allows users to clear entries they make in new expense report forms. In the OnClick event for this button, the clearFields private function resets all editable fields to their default state. The SetDirty method then sets the dirtiness bit to false, which allows users to exit the form without being prompted to save data. You can use this method when you want InfoPath to set hidden form fields during a submit operation.

Addressing Security Issues with Locally Saved Forms

It is important that you understand and address security issues associated with enabling Save and Save As functionality so that data integrity is maintained. Forms that a user saves to a local hard disk might be accessed by others, using InfoPath 2003 SP 1 or even a text editor. As a result, maintaining data integrity becomes an important issue in a scenario that allows users to save forms locally. The following sections identify measures you can take to help protect form data.

Implementing a Form Field

In the expense report scenario, you enabled Save and Save As calls to allow employees to record expenses while disconnected from the company network (for example, while at a client site). Users in this scenario can save and re-open forms so they can enter data as they incur expenses. Expense report reviewers, however, do not need to save forms locally. Their task is simply to review an employee's expenses and either approve or reject them. In addition, it is not practical to allow multiple copies of the same expense report form to exist on various local drives.

To limit Save and Save As functionality to expense report initiators only, you can implement a form field such as status. A status element identifies whether the expense report is New, Under Review, Approved, or Rejected and is modified by the BizTalk orchestration when routing logic is determined. Suppose initiators have access to the Save and Save As functionality of the expense report form when the status element value is New or Rejected. You can add the following code to the OnSaveRequest function to prevent reviewers from accessing the Save and Save As functionality.

public void OnSaveRequest(SaveEvent e)
{
    string formStatus = thisXDocument.DOM.selectSingleNode
        ("//my:status").text;
    if (formStatus == "New" || formStatus == "Rejected")
    {
        e.IsCancelled = e.PerformSaveOperation();
        e.ReturnStatus = true;
    }
}

Enabling Digital Signatures

Even with modifications to the OnSaveRequest function, users can still employ a text editor to modify expense report forms that are saved locally. You must use an additional feature, therefore, if you want to be more effective in maintaining form data integrity. Digital signatures is a feature of InfoPath 2003 SP 1 that you can use for this purpose.

If you enable digital signatures for an entire form or for sections within the form, users can apply a certificate to the applicable data. Then, each time a user opens the form in InfoPath, the application checks to ensure that the form has not been modified since the certificate was applied.

To enable digital signatures in a form

  1. On the Tools menu, click Form Options and then Digital Signatures.
  2. If you want the entire form to be signed, click Enable digital signatures for the entire form.
  3. If you want specific data in the form to be signed, perform the following steps:
    1. Click Enable digital signatures for specific data in the form.
    2. To create a set of signed data, click Add.
    3. In the Set of Signable Data box, provide a unique name for the signed data block.
    4. In the Fields and groups to be signed box, select the form element to be signed.
    5. Select a signature option and click OK. Repeat steps 3a through 3e for as many signed data blocks as the form requires.

After you enable digital signatures for the form, you can update the OnSaveRequest function to require an expense report initiator to digitally sign the form data before saving it locally. The following code expands the example in the previous section to check whether the entire form has been signed.

public void OnSaveRequest(SaveEvent e)
{
    string formStatus = thisXDocument.DOM.selectSingleNode
        ("//my:status").text;
    if (thisXDocument.IsSigned)
    {
        if (formStatus == "New" || formStatus == "Rejected")
        {
            e.IsCancelled = e.PerformSaveOperation();
            e.ReturnStatus = true;
        }
    }
    else
    {
        thisXDocument.UI.Alert("Forms must be digitally
            signed before they can be saved locally.");
    }
}

Encrypting and Decrypting Data

While you can enable digital signatures to help maintain data integrity, users can still use a text editor to view the content of a form. In our expense report scenario, allowing any user to view form content is not acceptable, because expense information is considered sensitive data. To help protect sensitive data, you can take the extra precaution of encrypting the data before a user can save a form locally. The following example shows how you can use the OnSaveRequest function to encrypt the total amount in the expense report form. In the example, the rKey object is initialized with the global declaration, private Rijndael rKey = new RijndaelManaged().

public void OnSaveRequest(SaveEvent e)
{
    // Data will not be encrypted in the case of a new
    // or auto-recovered form.
    if (thisXDocument.IsNew || thisXDocument.IsRecovered)
    {
        return;
    }
    // Encrypting the total amount.
    IXMLDOMNode totalNode = 
        thisXDocument.DOM.selectSingleNode("//my:Total");
    string totalNodeOriginal = totalNode.text;
    byte[] totalInfo = 
        System.Text.Encoding.Unicode.GetBytes(totalNode.xml);
    MemoryStream ms = new MemoryStream();
    CryptoStream csBase64 = new CryptoStream(ms,
         new ToBase64Transform(), CryptoStreamMode.Write);
    CryptoStream csRijndael = new CryptoStream(csBase64,
         rKey.CreateEncryptor(), CryptoStreamMode.Write);
    csRijndael.Write(totalInfo, 0, (int)totalInfo.Length);
    csRijndael.FlushFinalBlock();
    string base64enc = System.Text.Encoding.ASCII.GetString
        (ms.GetBuffer(),0, (int)ms.Length);
    // Adding XML encryption information to the form.
    totalNode.text = base64enc;
    e.IsCancelled = e.PerformSaveOperation();
    e.ReturnStatus = true;
    totalNode.text = totalNodeOriginal;
    thisXDocument.SetDirty(false);
}

In this example, the my:Total element value is encoded as a Base64 string prior to the save operation. After the operation is performed, this element is reset to its original value for display purposes, and the form's dirtiness bit is set to false, which allows the user to close the form without being prompted to save changes. When the user opens the expense report again in InfoPath, the OnLoad event handler is used to decrypt the expense data, as shown in the following example.

public void OnLoad(DocReturnEvent e)
{
    // Decrypting the total amount.
    IXMLDOMNode totalNode = 
        thisXDocument.DOM.selectSingleNode("//my:Total");
    byte[] totalNodeRes = Convert.FromBase64String(totalNode.text);
    MemoryStream msDec = new MemoryStream();
    CryptoStream csRijndaelDec = new CryptoStream(msDec,
         rKey.CreateDecryptor(), CryptoStreamMode.Write);
    csRijndaelDec.Write(totalNodeRes, 0, (int)totalNodeRes.Length);
    csRijndaelDec.FlushFinalBlock();
    string finalTotal = System.Text.Encoding.Unicode.GetString
        (msDec.GetBuffer(), 0, (int)msDec.Length);
    totalNode.text = finalTotal;
}

Note that to use this code, your project must use the using System.Security.Cryptography and using System.IO directives.

Conclusion

You can use the extended save functionality in InfoPath 2003 SP 1 to obtain programmatic access to Save and Save As calls. By using the OnSaveRequest callback function, you can override a form's default save functionality with the SaveEvent object properties and the PerformSaveOperation method. Other methods allow for some control of the Save As dialog box and provide access to the form's dirtiness bit. You examined these properties and methods in an expense report scenario.

You learned about security issues associated with allowing users to save forms locally, and you looked at several solutions. These solutions included implementing form fields, enabling digital signatures, and encrypting and decrypting form data. Any combination of these solutions helps to maintain form data integrity in a given scenario.

With the techniques you learned in this article, you can make InfoPath more versatile for your users while helping to maintain the integrity of sensitive data.

© Microsoft Corporation. All rights reserved.