This is the scond part of a two parts article.
The Cabinet documents created by the PopolaCabinets agent stores also the total binders numbers and the total documents number.
There are also two datetime fields used to store the start and the end time of the export.
The EsportaCabinet agent
Here follows the code for the EsportaCabinet agent: is the agent that create the file system structure and populates it with the Domino.Doc attachments.
Option Public Use "OpenLogFunctions" Dim FromArray(7) As String Dim ToArray(7) As String Sub Initialize 'Declare all API objects as Variants Dim theApi As Variant Dim theLibrary As Variant Dim theCabinets As Variant Dim theCabinet As Variant Dim theRooms As Variant Dim theRoom As Variant Dim theBinder As Variant Dim theField As Variant Dim theDocuments As Variant Dim theNewDocument As Variant Dim theProfile As Variant Dim isalink As Boolean Dim Db As NotesDatabase Dim doc As NotesDocument Dim ses As New NotesSession Dim fulldirpath As String On Error Goto errorHandler FromArray(0) = "/" FromArray(1) = ":" FromArray(2) = "*" FromArray(3) = "?" FromArray(4) = "<" FromArray(5) = ">" FromArray(6) = "|" FromArray(7) = "\\" ToArray(0) = "]" ToArray(1) = "-" ToArray(2) = "-" ToArray(3) = "-" ToArray(4) = "-" ToArray(5) = "-" ToArray(6) = "-" ToArray(7) = "-" 'Create the Api object Set theApi = CreateObject("DominoDoc.API") 'Connect to a domdoc server using Notes Call theApi.SetNotesLogin("-Lotus Notes ID password-") Set theLibrary = theApi.GetLibrary("notes://-server name-/-Domino.Doc library.nsf-") Call LogEvent("Started the export run", SEVERITY_LOW, Nothing) 'Get the Cabinets object from the Library Set theCabinets = theLibrary.Cabinets Dim c As Long c=theCabinets.Count Call LogEvent(Cstr(c) & " cabinets presenti", SEVERITY_LOW, Nothing) Set db = ses.CurrentDatabase Set collection = db.UnprocessedDocuments If collection.Count = 0 Then Msgbox "Selezionare almeno un cabinet da esportare!!", 16, "None Selected" Exit Sub End If Set doc = collection.GetFirstDocument While Not doc Is Nothing Call SetDate(doc, "LastEsportedStart") Call doc.Save(True, False) cabName$ = doc.cName(0) Set theCabinet = theLibrary.Cabinets(cabName$) Set theRooms = theCabinet.Rooms If theRooms.Count > 0 Then Set theRoom = theRooms.ItemByIndex(0) roomname$ = PulisciDirName(theRoom.Title) roompath$ = roomname$ & "\" If CreateDir("C:\domdoc\" & roomname$ ) Then Call LogEvent("Creata directory " & roomname$ & " per la room " & theRoom.Title, SEVERITY_LOW, Nothing) End If Else roompath$ = "" End If cabinetname$ =PulisciDirName(theCabinet.Title) cabinetpath$ = cabinetname$ & "\" thedir = CreateDir("C:\domdoc\" & roompath$ & cabinetname$ ) If CreateDir("C:\domdoc\" & roompath$ & cabinetname$ ) Then Call LogEvent("Creata directory " & cabinetname$ & " per il cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) End If 'Get binders Set theBinders = theCabinet.Binders 'Loop to get all Binders Dim x As Long x=theBinders.Count Call LogEvent(Cstr(x) & " binders presenti nel cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) For k = 0 To (x-1) Set theBinder=theBinders.ItemByIndex(k) Set theDocuments = theBinder.Documents y = theDocuments.Count directory$=theBinder.title Call LogEvent(Cstr(y) & " documenti presenti nel binder " & directory$ & " del cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) directory$ =PulisciDirName(directory$) category$ = "" Set theProfile = theBinder.Profile On Error Goto accessProfileHandler If Not(theProfile Is Nothing) Then If (theProfile.IsValid ) Then Dim h As Long h=theProfile.Fields.Count For m = 0 To (h-1) Set theField = theProfile.Fields.ItemByIndex(m) If theField.Name = "BinderCategory" Then category$ = theField.value End If Next End If End If resumeProfile: On Error Goto errorHandler If (category$ <> "") Then thedir = CreaDirFromCat( "C:\domdoc\" & roompath$ & cabinetpath$, category$) Call LogEvent("Create le directory " & thedir & " per le categorie del binder " & theBinder.Title & " del cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) fulldirpath = "C:\domdoc\" & roompath$ & cabinetpath$ & thedir & "\" & directory$ Else fulldirpath = "C:\domdoc\" & roompath$ & cabinetpath$ & directory$ End If If CreateDir(fulldirpath) Then Call LogEvent("Create la directory " & fulldirpath & " per il binder " & theBinder.Title & " del cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) End If ' loop to get all documents in the binder For i = 0 To ( y-1) Set theNewDocument = theDocuments.ItemByIndex(i) isalink = theNewDocument.IsLink If Not(isalink) Then docTitle$=theNewDocument.Title On Error Goto saveContentHandler If Not(theNewDocument.IsNew) Then Call ExportDocAndVersions(db, theNewDocument, fulldirpath, docTitle$, theBinder.Title, theCabinet.Title) End If resumeSavingContent: On Error Goto errorHandler Print fulldirpath & "-" & Cstr(i) Else Call LogEvent("Trovato il link - " & theNewDocument.Title & " - nel binder " & theBinder.Title & " del cabinet " & theCabinet.Title, SEVERITY_LOW, Nothing) End If Next Next Call SetDate(doc, "LastEsported") Call doc.Save(True, False) Set doc = collection.GetNextDocument(doc) Wend Call LogEvent("Finished the Export run", SEVERITY_LOW, Nothing) Exit Sub errorHandler: Call LogErrorEx("Errore grave nell'esportazione.", SEVERITY_HIGH, Nothing) Exit Sub saveContentHandler: On Error Goto 0 Call LogErrorEx("Errore nel salvataggio del file " & filename$, SEVERITY_MEDIUM, Nothing) Resume resumeSavingContent accessProfileHandler: On Error Goto 0 Call LogErrorEx("Errore nell'accesso al profilo del binder " & theBinder.Profile & " del cabinet " & theCabinet.Title, SEVERITY_MEDIUM, Nothing) Resume resumeProfile End Sub
The function PulisciDirName
This function replace the non permitted chars in filename with the ones specifed in the global ToArray string array. The function checks also for foler ending with one or more “.” (dot) and replace the trailing dot(s) with “_” (underscore).
Function PulisciDirName (dirname As String) As String On Error Goto processError Dim i As Integer Dim dot As String directory$ = dirname suffDir$ = directory$ i = 1 dot = "" While Right(suffDir$, 1) = "." dot = dot + "." suffDir$ = Left(directory$, Len(directory$) - i) i = i + 1 Wend directory$ = suffDir$ & dot directory$ =Replace(directory$, FromArray, ToArray) PulisciDirName = Trim(directory$) Exit Function processError: Call LogErrorEx("PulisciDirName failed", SEVERITY_LOW, Nothing) PulisciDirName = Trim(directory$) Exit Function End Function
The function NameShortner
If the full pathname is greater than 255 chars the Dir$ function crashes the notes client.
So before saving the attachment to disk we check the length and we shorten the filename if needed.
In order to obtain unique file name we use the passwordGenerator function to append a 2 chars random suffix.
Function NameShortner(fulldirpath As String, docTitle As String, allegato As String, v As Long, d As Long) As String Dim fullwithtitle As String Dim toShort As Integer fullwithtitle = fulldirpath & "\" & docTitle If Len(fullwithTitle) < 239 Then NameShortner = docTitle Else toShort = 250 - Len(fulldirpath) - 2 If toShort > 0 Then NameShortner = Left(docTitle, toShort) & passwordGenerator(2) While Not(Dir( fulldirpath & "\" & NameShortner)="") NameShortner = Left(docTitle, toShort) & passwordGenerator(2) Wend Else NameShortner = "" End If End If End Function
The function CreaDirFromCat
This function takes the binder categories and generates a file system path for these categories. It appends the _c suffix at each created folder.
Function CreaDirFromCat(sCurrDir As String, sCatName As String) As String On Error Goto processError Dim folders As Variant CreaDirFromCat = "" folders = Split(sCatName, "\") For x=0 To Ubound(folders) fold$ = folders(x) fold$ = PulisciDirName(fold$) sCurrDir = sCurrDir & "\" & fold$ & "_c" ret=CreateDir(sCurrDir) CreaDirFromCat = CreaDirFromCat & "\" & fold$ & "_c" Next CreaDirFromCat = Right(CreaDirFromCat, Len(CreaDirFromCat)-1) Exit Function processError: Call LogErrorEx("CreaDirFromCat failed", SEVERITY_LOW, Nothing) CreaDirFromCat = "" Exit Function End Function
The function GetExt
It returns the filename extensions.
Function GetExt(path As String) As String On Error Goto errorHandler Dim table As Variant Dim fileName As String fileName = path If Instr(fileName, "\") > 0 Then table = Split(Trim(fileName), "\") fileName = table(Ubound(table)) End If If Instr(fileName, ".") > 0 Then table = Split(fileName, ".") GetExt = "." & table(Ubound(table)) Else GetExt = ".xxx" End If Exit Function errorHandler: Call LogErrorEx("GetExt failed " & path , SEVERITY_MEDIUM, Nothing) GetExt = ".xxx" Exit Function End Function
The function SetDate
Set the specified field of the specified document with the current date/time.
Function SetDate(doc As NotesDocument, strFieldName As String) Dim tdate As New NotesDateTime(Now()) Call doc.ReplaceItemValue(strFieldName, tdate) End Function
The sub ExportDocAndVersions
To be descripted.
Sub ExportDocAndVersions(db As NotesDatabase, theDocument As Variant, fulldirpath As String, docTitle As String, Binder As String, Cabinet As String) On Error Goto processError Dim revHistDocs As Variant Dim revHistDoc As Variant Dim k As Long Dim v As Long Dim d As Long Dim IsVersion As Boolean Dim docTitle_ori As String docTitle = PulisciDirName(docTitle) Set revHistDocs = theDocument.RevisionHistoryDocuments k = revHistDocs.Count If (k>0) Then For i = 0 To (k-1) Set revHistDoc = theDocument.RevisionHistoryDocuments.ItemByIndex(i) IsVersion = theDocument.IsVersion 'If IsVersion Then attach$=revHistDoc.Filename v = revHistDoc.VersionNumber d = revHistDoc.DraftNumber filename$=fulldirpath &"\" & docTitle &"_-_" & AppendVersion(attach$, v, d) If (Len(filename$) < 250) Then If attach$ <>"" And Dir$(filename$) = "" Then Call revHistDoc.GetContents(filename$) Call LogDoc(db, revHistDoc, filename$, docTitle, Binder, Cabinet, "") End If Call LogEvent("Esportazione versione - " & attach$ & " - nel path " & filename$, SEVERITY_LOW, Nothing) Else docTitle_ori = docTitle docTitle = NameShortner(fulldirpath, docTitle, attach$, v, d) filename$=fulldirpath &"\" & docTitle & "_ver" & Cstr(v) & "-" & Cstr(d) & "." &GetExt(attach$) If (docTitle = "") Then Call LogDoc(db, revHistDoc, filename$, docTitle, Binder, Cabinet, "path troppo lungo") Call LogEvent("NESSUNA esportazione versione - " & attach$ & " - nel path " & filename$, SEVERITY_LOW, Nothing) docTitle = docTitle_ori Else Call revHistDoc.GetContents(filename$) Call LogDoc(db, revHistDoc, filename$, docTitle_ori, Binder, Cabinet, "path accorciato: " & docTitle) End If End If Next End If Exit Sub processError: Call LogErrorEx("ExportDocAndVersions failed", SEVERITY_LOW, Nothing) Exit Sub End Sub
The function CreateDir
Nothing to say.
Function CreateDir(sDirName As String) As Boolean On Error Goto processError Const ATTR_DIRECTORY = 16 sDirName = Trim(sDirName) If Trim(Dir(sDirName,ATTR_DIRECTORY ))="" Then Mkdir(sDirName ) CreateDir = True Else CreateDir = False End If Exit Function processError: Call LogErrorEx("PulisciDirName failed", SEVERITY_LOW, Nothing) CreateDir = False Exit Function End Function
The function LogDoc
This function creates a notes document with the Document form for each exported attachment.
Function LogDoc(db As NotesDatabase, theNewDocument As Variant, filename As String, masterDocTitle As String, binder As String, cabinet As String, reason As String) On Error Goto processError Dim HasDraft As String Dim HasReviewCopy As String Dim HasVersion As String Dim IsCurrentRevision As String Dim IsLocked As String Dim IsReviewCopy As String Dim IsVersion As String Dim IsWorkingCopy As String Dim Title As String Dim Id As String Dim UniversalId As String Dim doc As NotesDocument If theNewDocument.HasDraft Then HasDraft = "1" Else HasDraft = "0" End If If theNewDocument.HasReviewCopy Then HasReviewCopy = "1" Else HasReviewCopy = "0" End If If theNewDocument.HasVersion Then HasVersion = "1" Else HasVersion = "0" End If If theNewDocument.IsCurrentRevision Then IsCurrentRevision = "1" Else IsCurrentRevision = "0" End If If theNewDocument.IsLocked Then IsLocked ="1" Else IsLocked ="0" End If If theNewDocument.IsReviewCopy Then IsReviewCopy = "1" Else IsReviewCopy = "0" End If If theNewDocument.IsVersion Then IsVersion = "1" Else IsVersion = "0" End If If theNewDocument.IsWorkingCopy Then IsWorkingCopy = "1" Else IsWorkingCopy = "0" End If Title = theNewDocument.Title Id = theNewDocument.Id UniversalId = theNewDocument.UniversalId Set doc = db.CreateDocument doc.Form ="Document" Call SetDate(doc, "LastExported") doc.dHasDraft = HasDraft doc.dHasReviewCopy = HasReviewCopy doc.dHasVersion = HasVersion doc.dIsCurrentRevision = IsCurrentRevision doc.dIsLocked = IsLocked doc.dIsReviewCopy = IsReviewCopy doc.dIsVersion = IsVersion doc.dIsWorkingCopy = IsWorkingCopy doc.dTitle = Title doc.dId = Id doc.dUniversalId = UniversalId doc.dBinder = binder doc.dCabinet = Cabinet doc.dFilename = filename doc.dDraft = theNewDocument.DraftNumber doc.dVersion = theNewDocument.VersionNumber doc.dReason = reason doc.dMasterDocTitle = masterDocTitle Call doc.Save(True, False) Exit Function processError: Call LogErrorEx("PulisciDirName failed", SEVERITY_MEDIUM, Nothing) LogDoc = "" Exit Function End Function
The function passwordGenerator
Used to generate a random suffix
Function passwordGenerator(Byval lngLength As Long) As String ' Description: Generate a random password of 'user input' length ' Parameters : lngLength - the length of the password to be generated ' Returns : String - Randomly generated password ' Created : 08/21/1999 Andrew Ells-O'Brien (andrew@ellsobrien@msn.com) ' Updated : Rocky Oliver, Sapphire Oak Technologies (rock@sapphireoak.com) ' made it work in LotusScript On Error Goto errHandler Dim iChr As Integer Dim c As Long Dim strResult As String Dim iAsc As String Randomize Timer For c = 1 To lngLength ' Randomly decide what set of ASCII chars we will use iAsc = Int(3 * Rnd + 1) 'Randomly pick a char from the random set Select Case iAsc Case 1 iChr = Int((Asc("Z") - Asc("A") + 1) * Rnd + Asc("A")) Case 2 iChr = Int((Asc("z") - Asc("a") + 1) * Rnd + Asc("a")) Case 3 iChr = Int((Asc("9") - Asc("0") + 1) * Rnd + Asc("0")) Case Else Error 1000, "PasswordGenerator has a problem." End Select strResult = strResult & Chr(iChr) Next c PasswordGenerator = strResult getOut: Exit Function errHandler: Call LogErrorEx("PasswordGenerator failed " & path , SEVERITY_MEDIUM, Nothing) PasswordGenerator = "" Resume getOut End Function
The function AppendVersion
This function append the version and draft number at the exported filename suing this pattern: _ver-.
Function AppendVersion(path As String, Version As Long, Draft As Long) As String On Error Goto errorHandler Dim table As Variant Dim fileName As String Dim RemoveFileExtension As String fileName = path If Instr(fileName, "\") > 0 Then table = Split(Trim(fileName), "\") fileName = table(Ubound(table)) End If If Instr(fileName, ".") > 0 Then table = Split(fileName, ".") AppendVersion = Left(path, Len(path) - Len(table(Ubound(table))) - 1) & "_ver" & Cstr(Version) & "-" & Cstr(Draft) & "." & table(Ubound(table)) Else AppendVersion = path & "_ver" & Cstr(Version) End If Exit Function errorHandler: Call LogErrorEx("AppendVersion failed " & path & "-" & Cstr(Version) & "-" & Cstr(Draft), SEVERITY_MEDIUM, Nothing) AppendVersion = path Exit Function End Function
How does it works?
On the C drive of the PC folder create the domdoc folder.
Here you’ll store the exported attachments.
From this folder an agent will create a directory tree based on the hierarchy of Domino.Doc
- Rooms (folder is always present – no documents)
- Cabinet (folder is always present – no documents)
- Folders categories (folder(s) is(are) optional(s) – no documents)
- Binder Folder (folder is always present – any document can be found here)
- Documents
For exporting documents and their versions the following naming scheme will be used:
<document-title> _-_ _ <filename_without_extension> _ver<version>-<draft>.<extension>
- <document-title> is the title that was given to the document in Domino.Doc. Most of the time coincides with the filename attachment without the extension.
- <filename> is the name of the attachment
- <version> is the version number
- <draft> is the number of draft
To support the export procedure you create two Lotus Notes database.
The first, APPL (edomdocs-v1.nsf), “supervises” the export process.
The second LOG (OpenLog1_5Apache.nsf) is the log of the various exports.
APPL contains an agent (PopolaCabinets) that examines the structure of Domino.Doc and creates so many documents for those cabinets to be exported.
For each cabinet is gathered a wealth of information, including the number of binders and documents it contains.
Then selecting one or more cabinets from the Cabintes view in Lotus Notes client, you can launch its export. The choice is due to the fact that the procedure is time intensive and also it is believed that many cabinet does not need to be exported.
After starting the export a document will be created in the same database (one document for each of the exported files).
If the document can not be exported because its fullpathname exceeds the limit of 250 characters, the document created in APP report the event and it trys to “shorten” the exported filename.
NOTES: The titles of ROOMS, CABINETS and the categories were “filtered” by replacing illegal characters with the character _The / character (often used in these names) has been replaced with the character].
The procedure always checks the presence of fullpathname: if the directory or file already exist, they are overwritten. This is not true for “shortned” filename because we generate a random file name.
Here you can download the Lotus Notes app: [download id=”3″]