Implementing Change Tracking for InfoPath Forms

Although InfoPath doesn't have a Change Tracking feature built-in like Word and some of the other Office System applications, read on for guidance on how to implement it in your own solutions.

There are several problems to decide before jumping into implementation: where do you want to store change tracking information, how do you map between list of changes and elements, tracking information protection, and what format you use to store change tracking information.

Storage options

Depending on schema of XML there are several options:

  1.  Closed schema - public or required by third party.
    1. "Envelope" approach - you can store whole document as XML sub tree inside your own XML with open schema.
    2. Using Processing Instructions to store data is possible when the schema allows it and consumers of XML can handle processing instructions correctly. Note that even if one suppose to ignore them it is not always the case.
    3. Separate file. Just keep tracking information somewhere (I.e. local file or remote Web service). This approach is remotely reasonable if you know how to associate data with tracking information.
  2. Open schema
  • Store tracking information as single attribute.
  • Store tracking information as sub-tree of each element that is interesting for change tracking
  • Store tracking information in single node providing some mapping between elements you are interested in and tracking information.

Format options

 1. XML friendly.

If you want your tracking information to be processed using some form of normal XML handling (XSL, XML readers, XPath navigation) then you should use XML sub-trees to store change tracking information.

For example:

 <changes>
<change forElement="element1233" date="2004-06-12" author="john" type="textChange">New text</change>
<change forElement="element1233" date="2004-06-14" author="fred" type="textChange">Don't writen new text!</change>
</changes>

 2. Custom text format

 If you can't use normal XML for some reasons (i.e. storage options 1.b, 2.a) then you can develop text format to store the data. You can use approach close to XML (i.e. just store sub-tree as string) or even go with Base64 encoded binary.

Mapping between element and tracking information

 1. Direct access.

 If you can put tracing information right into element you are interested in then you don't need any mapping.

 Note that this approach requires you to think through deletion scenario because the element itself might be deleted along with it's tracking information.

 2. Element ID. 

This is an easy option to implement if you have or can establish persistent unique element identifiers for each element you want to track changes. It could be attribute on elements or some combination of attributes and values of element.

Note that you can not use XPath to element as unique id if you have any repeating content you want to track changes on.

The problem here is that you need to have good ID and should think through insertion of elements.

3. Position based mapping.

This is the most complex option to implement. If you can't have any identifiers for element you had to go with position based mapping. I.e. 

<change forElement="/root/items[0]/item[4]/component[3]" date="2004-06-12" author="john" type="textChange">New text</change>

where forElement contains the XPath to the element of interest.

The main problem here is that you have to dynamically update all mappings on element insertions through the tree.

Change tracking information protection

You should consider if and how you want to protect this data. Approaches would vary from digitally signing each change (or editing session) to "who cares if it is changed?"

Coding ideas for simplest case

You have unique element IDs that somehow get generated for elements. You are interested in recording changes in text value of an element.

Here is sample of XML you want to record changes for:

<root>
<items>
<item id = "12"><textToTrack>text</textToTrack></item>
<item id = "23"><textToTrack>text</textToTrack></item>
<item id = "55"><textToTrack>text</textToTrack></item>
</items>
</root>

The approach choosen to store the data: separate from the data as subtree of the <root> node, and no protection and tamper detection for tracking information.

Here is example of XML with tracking information.

<root>
<changes>
<change forElement="12" date="2004-06-12" author="john" type="textChange" ><old>text</old><new>Shining new text</new></change>
<change forElement="12" date="2004-06-14" author="fred" type="textChange" ><old>Shining new text</old><new>Don't write new text!</new></change>
<change forElement="23" date="2004-06-14" author="fred" type="delete"></change>
</changes>
<items>
<item id="12"><textToTrack>Don't write new text!</textToTrack></item>
<item id="55"><textToTrack>text</textToTrack></item>
</items>
</root>

To code this in InfoPath you need:

Make sure schema allows <changes> to be inserted. I.e. In designer for new solution create this structure in the Data Source task pane.

Figure out how to get user name if needed.

Add an "OnAfterChange" listener for <items> node, and implement the following pseudo-code:

 IF change = "add" AND node-name = "item"
make sure ID is generated for "add"
record (ID, added) information.
IF change = "remove" AND node-name = "item"
record (ID, deleted)
IF (change = "add OR change = "remove" OR change = "change") AND node-name = "textToTrack"
record (ID, textChange, oldValue, newValue)
  

Where ID is "id" attribute of corresponding "item" node, and record means append all needed information a <changes> collection. Record function should also take care of merging change information for the same date/user like if node was added in this session all changes should be combined into "added" record, rather then creating multiple "changed" sections.

Note that "undo" user actions will rollback change as well as tracking information. If you store data in separate file you should take care of undo yourself.