SnTT: The ins and outs of ILDDM Binder and Document Security

October 26 2006

ShowAndTell.jpg

I have not done a SnTT post in a while, but I figured this one was something people could use. For the past couple of months, I have been waging a war against IBM Lotus Domino Document Manager ... otherwise known and Domino.Doc. I am working on a client project that is built around the New Product submission idea, but using ILDDM as the way to store the documents. We have lots of documents in the system, from Office to editable PDFs to artwork designed on the Mac to Autocad drawings. Our system allows for the attachments to be put thru workflow (for comments, editing, and approval), but we store everything in ILDDM. Because of the lack of robust workflow in ILDDM, we are using our own enTouch.workflow product. What that means is that we have a document stub that goes thru our workflow, which links to the ILDDM document. Because of the nature of this process, security is very tight. Add in the difficulty of no ILDDM API on the Mac (today) and the lack of any good way to set security on Binders, I decided to do it my own way.
When working with ILDDM, the user sees a Binder as a folder. What a Binder is really is a document in the Binder database. Documents are stored in the Document database.The user just sees two databases on the workspace, with the Document database appending a 1 to the database name.

So what I have done is found a way to set the security on the Binder document and all of the Document documents directly. There is a couple of tricks.

First, I go thru the typical motions of adding an attachment to ILDDM. You do the following:

  • Connect to the ILDDM API via the "Set API = CreateObject("DominoDoc.API")"
  • Connect to the Library via "Call API.SetHttpLogin(ILDDM_UserName, ILDDM_Password)"
  • Connect to the Library via "Set Library = API.GetLibrary(library)"
  • Connect to the File Cabinets collection via "Set FileCabinets = Library.Cabinets"
  • Connect to the File Cabinet via "Set FileCabinet = FileCabinets.ItemByTitle(fcname)"
  • Connect to the Binders collection via "Set Binders = FileCabinet.binders"
  • Connect to the Binder via "Set Binder = Binders.ItemByTitle (bindername)"
  • Connect to the Documents collection via "Set Documents = Binder.Documents"
  • Create a new document via "Set Document = Documents.Add"
  • Set the attachment (need the full path to the attachment) via "Call Document.SetContents(path)"
  • Set the Document Title via "Document.Title = title"
  • Set the Document Description via "Document.Description = desc"
  • Save the Document via "Call Document.Save"
  • Get the Security object of the Document via "Set Binder_Security = Binder.Security"
  • IMPORTANT: Set the All Editors flag to false via "Binder_Security.AllEditorsFlag = False"
  • IMPORTANT: Set the All Readers flag to false via "Binder_Security.AllReadersFlag = False"
  • Save the Document gain via "Call Document.Save"
  • Check in the Document via "Call Document.CheckIn(1, 2, False, "")"

Once we have the document in ILDDM, I write the following information back to the stub document in our workflow system:


With document
               .ILDDMServer = FileCabinet.NotesServer
               .ILDDMBinderDatabaseRepID = FileCabinet.BinderDatabase
               .ILDDMDocumentDatabaseRepID = Binder.DocumentDatabase
               .ILDDMBinderDocumentUID = Binder.UniversalID
               .ILDDMDocumentDocID = Document.UniversalID
              
               Dim docdb As New notesdatabase("","")
              
               Call docdb.OpenByReplicaID(FileCabinet.NotesServer,Binder.DocumentDatabase)
              
               .ILDDMDOcumentDatabaseFileName = docdb.FilePath
End With


This gives me all of the information I need to access the ILDDM Server, the Binder Database, the Binder document, the Document database, and the Document itself without having to use the ILDDM API.

Now, comes the fancy piece. I call an agent in the Binder database that I have created and pass in the workflow stub document as a parameter. Our workflow engine computes the security and writes it into two fields, one Reader and one Author. The agent looks like this (I took out some of the pieces, this is just the relavant piece):


binderserver = document.ILDDMServer(0)
If binderserver = "" Then
        Gotobye
End If

binderdb_value = document.ILDDMBINDERDATABASEREPID(0)
If binderdb_value = "" Then
        Gotobye
End If

documentdb_value = document.ILDDMDOCUMENTDATABASEREPID(0)
If documentdb_value= "" Then
        Gotobye
End If

binderdoc_uid = document.ILDDMBINDERDOCUMENTUID(0)
If binderdoc_uid = "" Then
        Gotobye
End If

Set readerfield = document.GetFirstItem("SFDocReaders")
Set authorfield = document.GetFirstItem("sfdocauthors")

Call binderdb.OpenByReplicaID(binderserver,binderdb_value)
If binderdb.IsOpen = False Then
        Print"Binder Database not opened"
        Gotobye
End If

Call documentdb.OpenByReplicaID(binderserver,documentdb_value)
If documentdb.IsOpen = False Then
        Gotobye
End If

Set binderdoc = binderdb.GetDocumentByUNID(binderdoc_uid)
If binderdoc Is Nothing Then Goto bye
     
Call binderdoc.removeitem("ComputedAuthors")
Call binderdoc.RemoveItem("DocAuthors")
Call binderdoc.RemoveItem("ComputedReaders")
Call binderdoc.removeitem("DocReaders")
       
binderdoc.computedauthors = document.sfdocauthors

binderdoc.docauthors = document.sfdocauthors
binderdoc.computedreaders = document.sfdocreaders
binderdoc.docreaders = document.sfdocreaders

Set binder_computedreaders_item = binderdoc.GetFirstItem("ComputedReaders")
Set binder_computedauthors_item = binderdoc.GetFirstItem("ComputedAuthors")

Call binder_computedreaders_item.AppendToTextList("[Administrator]")

binder_computedreaders_item.IsReaders True
binder_computedauthors_item.IsAuthors True

Call binderdoc.Save(True, False)
'update documents

document.sfdocauthorreader = Arrayappend(document.sfdocauthors,document.sfdocreaders)
document.sfdocauthorreader = Arrayunique(document.sfdocauthorreader)
document.sfdocauthorreader = Fulltrim(document.sfdocauthorreader)

Set document_computedreaders_item = document.GetFirstItem("sfdocauthorreader")

Call document_computedreaders_item.AppendToTextList("[Administrator]")
'update versions        

Set docview = documentdb.GetView("vaDocsByBinderID")
Set doccol = docview.GetAllDocumentsByKey(binderdoc_uid)

If doccol.Count > 0 Then
        Calldoccol.StampAll ("ComputedAuthors", document.sfdocauthors)
        Calldoccol.StampAll("ComputedReaders", document.sfdocauthorreader)
End If
'update drafts

Set draftview = documentdb.GetView("DraftDocsByBinderID")
Set draftcol = draftview.GetAllDocumentsByKey(binderdoc_uid)

If draftcol.Count > 0 Then
        Calldraftcol.StampAll ("ComputedAuthors", document.sfdocauthors)
        Calldraftcol.StampAll("ComputedReaders", document.sfdocauthorreader)
        Calldraftcol.StampAll ("ComputedDraftEditors", document.sfdocauthors)
End If


So as you can see, I set the Binder document security manually and update all of the documents with the correct fields. Since ILDDM handles the database ACL itself, I do not have to worry about that.

I only had to create one new Design Element in ILDDM for this, and that is the "DraftDocsbyBinderID" view. That view is just a copy of the "vaDocsByBinderID" with the Selection formula changed to include "& @IsResponseDoc".

While this is definitly a righteous hack as Rocky would say, it works perfectly. Next up is to figure out how to create a File Cabinet programatically, since the ILDDM API does not include that.