Find Added To File (Part 1)
Have you ever dealt with someone having a local replica of a database and not replicating it on a regular basis? Then you have probably seen a lot of previously deleted documents "reappear" on the server. When it does happen, you can look at the "added in this file" document property (figure 1) to see when the actual document was created on the server. Unfortunately, this property is not available to LotusScript. But it is listed when you look at the XML of the document. This tip is the first in a two-part series showing how to get this value programmatically. If you have never worked with an XML tree in Domino before, this is going to be a bit involved. If you have worked with it before, then you'll see some familiar stuff, but it may or may not be in the same way you've done it before. The main thing to keep in mind is that an XML record is a tree structure. For example, let's look at one document. The document itself is the root of the tree. The document has properties (like "added in this file") which is a node of the root. Another type of node is a field inside the document. But that field also has properties (field name, field type, field value), which are nodes themselves. This hierarchy has to be parsed in order to read what we want.
First off, let's start by creating a new agent. Call the agent whatever you want (I called mine "Find Added To File"). Set up the agent to run manually from the actions menu and set the trigger to "None". (Incidentally, this agent is a good candidate for running in a background thread - read this document to find out more).
Although I normally don't condone global variables, I've found that parsing XML is a lot easier to implement with a global variable. This agent uses recursion and it's much "cleaner" with a global variable. Only one global variable is used.
(Declarations)
Dim domParser As NotesDOMParser
This class takes input XML and generates a standard Document Object Model (DOM) tree structure. So I'm putting the cart before the horse a little bit here - we haven't even generated the XML yet. We'll get into this class more later on in this tip, and much more in the next tip.
The flow of this agent is to get the documents to process, then build the XML for those documents, then convert that XML to a DOM tree structure, then traverse the tree. So let's start by getting the documents to process:
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim coll As NotesNoteCollection
Dim exporter As NotesDXLExporter
Dim inputStream As NotesStream, outputStream As NotesStream
Dim docNode As NotesDOMDocumentNode
Dim xmlHierarchy As Variant
Dim doc As NotesDocument
Set db = session.CurrentDatabase
Set inputStream = session.CreateStream
Set outputStream =session.CreateStream
Set coll = db.CreateNoteCollection(False)
coll.SelectDocuments = True
Call coll.BuildCollection
If coll.Count = 0 Then Exit Sub
There might be a couple of unfamiliar objects here. The first is NotesNoteCollection. This is very similar to a standard document collection, except it can hold documents or design elements. The two NotesStream variables are used as "flow through points" going from the actual documents to the XML and then to the DOM tree structure. The NotesStream class is used to represent a stream of data, so it has lots of uses (reading data from the file system, writing data to memory before putting it somewhere, etc.). The NotesDOMDocumentNode is the root of our DOM tree structure. So it is basically the entire XML document.
The agent gets a handle to the current database and establishes the two stream objects. The note collection is established. The parameter of False indicates that not everything in the database should be selected. If this was True then all documents and all design elements would be in the collection. We only care about the documents. So the next statement says to select the documents. There are lots of properties - you can specify exactly certain design elements if you want. The collection is then built. If there are no documents in the collection, then the agent exits since there's nothing for it to do.
The next thing to do is take that collection and create XML out of it. This is surprisingly easy to do:
Set exporter = session.CreateDXLExporter(coll, inputStream)
Call exporter.Process
At this point the NotesStream object called inputStream contains all the XML from the documents in the collection. I'll point out that, depending on how big the collection is, this could be a big memory hog.
That XML needs to be converted to the DOM that can be parsed programmatically. That is done through the following statements:
Set domParser=session.CreateDOMParser(inputStream, outputStream)
Call domParser.Process
Set docNode = domParser.Document
The last statement gives us a pointer to the highest level point in the DOM hierarchy. We can now go through that DOM tree and find the "added to file" property on the documents:
Redim xmlHierarchy(0) As String
Call WalkTree(db, docNode, xmlHierarchy, doc)
Set docNode = Nothing
Set domParser = Nothing
Call outputStream.Truncate
Call outputStream.Close
Call inputStream.Truncate
Call inputStream.Close
End Sub
The WalkTree subroutine is going to be discussed next week. I will say that this is a recursive subroutine that is very similar to what can be found in the Domino Designer Help database. The xmlHierarchy variable helps us know what level of recursion is currently being observed - I'll discuss that next week.
Once the DOM hierarchy has been processed, a little cleanup is done. I set the variables to Nothing and truncate and close the streams.