1

I built an application in C# that copies documents from a source NSF to a destination NSF. The destination NSF is an empty shell, retaining all design elements, based on the source NSF. I am using Lotus Notes 8.5.3 and am not connected to a Domino Server.

I use this application to split the source NSF into smaller chunks. The goal is to create destination NSFs that can be handled effectively by our automated (eDiscovery) systems. I need to ensure that as much metadata as possible are preserved.

My existing code meets these goals, except that that (1) I lose foldering information. After copying documents, all folders are empty. (2) All documents are marked as Read, even if they were unread in the source.

Code C#

//Establish session
NotesSession ns = new Domino.NotesSessionClass();
ns.Initialize("");

//Open source NSF
NotesDatabase nd = ns.GetDatabase("", "test.nsf", false);
//Open destination NSF.
//Assume that all design elements of nd2 are identical to those of nd
NotesDatabase nd2 = ns.GetDatabase("", "test2.nsf", false);

//Create view that returns all documents.
NotesView nView2 = nd.GetView("$All");
nd.CreateView("All-DR", "SELECT @ALL", nView2, false);
NotesView nView = NotesConnectionDatabase.GetView("All-DR");

//Loop through entries in the new view
NotesViewEntry nvec = nView.AllEntries;
nve = nvec.GetFirstEntry();

for (int j = 1; j <= intEntryCount; j++)
{
     if (j == 1)
     {
          nve = nvec.GetFirstEntry();
     }
     else
     {
          nve = nvec.GetNextEntry(nve);
     }

     //Copy document to second database.
     NotesDocument ndoc = nd.GetDocumentByUNID(nve.UniversalID);         
     ndoc.CopyToDatabase(nd2);
 }
 //End loop.
 //All documents are copied.

The result is that I end up with a destination NSF that has all the documents copied over. Assume that all the folders are also there. However, none of the documents are in the folders. Every document is marked as read.

How can I fix the folders and unread issue?

Jacob Quisenberry
  • 1,131
  • 3
  • 20
  • 48
  • Since unread marks are tracked by user, have you tried opening those destination database with an ID other than the one used to do the copying to see if the documents show as unread to other users? – David Navarre Feb 08 '13 at 16:07
  • I have verified that if an NSF is opened with a different ID file, all documents will appear unread. – Jacob Quisenberry Feb 18 '13 at 23:50

2 Answers2

5

There is a FolderReferences property in the NotesDocument class in the back-end classes. I'm not 100% sure if that property is exposed in the COM classes and interop for C#, but if it is, you can use that along with the PutInFolder() method to solve part of your problem.

As far as read/unread marks are concerned, the critical question is whether you are concerned only about the read/unread status for yourself, or whether you are trying to preserve it for all users of the database. If you only care about unread marks for yourself, then you might be able to use the getAllUnreadDocuments() method of the NotesDatabase class -- but this requires Notes/Domino 8 or above (on the machine where your code is running), and again I'm not sure if this method (or the NotesNoteCollection class that it returns) is exposed via the COM/interop interface for C#. If it is available, then you can iterate through the collection and use the MarkUnread() method. If you care about unread marks for all users, then I'm not sure if there is a way to do it at all -- but if there is, it's going to require using calls from the Notes C API.

Richard Schwartz
  • 14,463
  • 2
  • 23
  • 41
  • Since PutInFolder() and MarkUnread() are methods of the NotesDocument class, I would have to call them on the document in the destination NSF after copying it from the source. How can I determine which document I just copied over? Alternatively, how can I determine which document in the source NSF a given document in the destination NSF corresponds with? Is it expected that NotesDatabase.AllDocuments.GetLastDocument() would work? – Jacob Quisenberry Feb 08 '13 at 17:56
  • 1
    If you're copying documents one at a time, you would presumably have a handle to the doc in the new database immediately after you copied it. I.e., use doc2 =doc1.CopyToDatabase(destDb), then check the FolderReferences property of doc1, and use doc2.PutInFolder(). – Richard Schwartz Feb 08 '13 at 18:17
  • If for some reason you need to do all the copying first, and then go back through the copied docs to set the folders and unread marks, and if your application doesn't have a NotesItem that contains a unique identifier for each doc, then you're going to have to build a list of noteid pairs while doing the copying, and iterate through that list afterward to go back and set the folder and unread properties. – Richard Schwartz Feb 08 '13 at 18:18
  • 1
    Ken's solution, which doesn't assume folderReferences is available, is using UNID instead of noteid. Note that in some older versions of Notes, the UNID was preserved by the CopyToDatabase method, but that's no longer true -- so just bear in mind that you cannot use nd2.getDocumentByUNID to find the corresponding document. – Richard Schwartz Feb 08 '13 at 18:24
  • In order to use FolderReferences, I have to copy views ($FolderInfo) and ($FolderRefInfo) and enable FolderReferencesEnabled. Is this something I can do immediately before running my existing code? Would I need to do anything to get the views to update. I expect that NotesView.Refresh() would do the job. – Jacob Quisenberry Feb 08 '13 at 18:29
  • This is going a bit too far back in my memory. I'm not sure, but I think that there's more to it than just a refresh. If I recall correctly, you might have to run the convert utility in order to initially populate the references into the views -- but I'm just not sure. – Richard Schwartz Feb 08 '13 at 19:09
  • It does not appear that MarkUnread() is available when using C#. The compiler is aware of methods like MakeResponse and PutInFolder but not MarkUnread(). I see that MarkUnread is listed in the Java and LotusScript documentation? Is there a trick to making MarkUnread available? Is there an alternative? – Jacob Quisenberry Feb 12 '13 at 17:48
  • 1
    There's a Notes C++ API, and according to this older StackOverflow thread it looks like that's your alternative: http://stackoverflow.com/questions/1870958/how-do-i-get-set-domino-read-status-of-messages-via-domino-com-object The Notes C API is another alternative, but it's very low level so it requires a pretty deep understanding of what's going on inside the NSF. – Richard Schwartz Feb 12 '13 at 18:53
  • Richard, I have come to believe that the C++ API is the solution I require to copy Unread marks. Thank you. – Jacob Quisenberry Feb 14 '13 at 06:58
2

Another thought on moving to folders, especially if the database isn't set up for FolderReferences to work:

You can iterate over the array of NotesView objects obtained from the NotesDatabase object's Views property. Each NotesView has a property that tells you if it is a folder.

Once you know about all the folders, you can iterate within each folder and collect a list of NotesDocuments that are contained within. Then by storing this information in a dictionary you could use this as a lookup while you process each document to decide what folder(s) it needs to be placed in.

Something like this (not tested):

object oViews = nd.Views;
object[] oarrViews = (object[])oViews;
Dictionary<string, List<string>> folderDict = new Dictionary<string, List<string>>();
for (int x=0; x < oarrViews.Length - 1; x++)
{
    NotesView view = viewArray[x];
    if (view.IsFolder) 
    {
        NotesDocument doc = view.GetFirstDocument();
        while (doc != null)
        {
            // Populate folderDict Dictionary by setting
            // document's UNID as Key, and adding folder name to List
        }
    }
}

Then in your loop:

//Copy document to second database.
NotesDocument ndoc = nd.GetDocumentByUNID(nve.UniversalID);  
NotesDocument newDoc = ndoc.CopyToDatabase(nd2);

if (folderDict.ContainsKey(nve.UniversalID)) {
    foreach (var folderName in folderDict[nve.UniversalID]) {
        newDoc.PutInFolder(folderName, true);
    }
}       
Jacob Quisenberry
  • 1,131
  • 3
  • 20
  • 48
Ken Pespisa
  • 21,989
  • 3
  • 55
  • 63
  • 3
    Ah, yes. I forgot to mention that the database has to be set up for FolderReferences to work. And honestly, it rarely is. – Richard Schwartz Feb 08 '13 at 15:01
  • It looks like your second code block answers my question about how to identify which document in the destination databases corresponds to a given document in the source database. – Jacob Quisenberry Feb 08 '13 at 18:02