Object Oriented Example – Document Locking
If Toohey is still posting LotusScript tips in his blog… I will too.
Today I am showing a very simple document locking class. If you don’t care about Object Orientated code in LotusScript or you’ve seen enough examples… feel free to move along.
What is the class?
This is a very simple class to assist with document locking. There are so many ways to do locking in Lotus Notes, but I use this one because it’s quick and easy.
What do I need besides the class?
The only thing this class needs is a view. I’ve put all of the parameters of the view in the Constants section of my script library:
Const LOCK_VIEW = "($$Lock)"
Const LOCK_FORM = "zzLock"
Const LOCK_USERFIELD = "username"
Const LOCK_UNIDFIELD = "unid"
Const LOCK_DELETEVAL = "~~DELETEME~~"
The selection formula of the view is “form = “zzLock”" and it has two columns: unid and username.
What’s the huge flaw in your code?
I couldn’t figure out how to use this class with a group who has Author access but not create document rights. Luckily… I’ve never had to deal with that situation. Any ideas would be appreciated.
How do you use the class in a form or agent?
Here’s an example of what I do in the QueryModeChange event of a form.
Sub Querymodechange(Source As Notesuidocument, Continue As Variant)
Dim locktool As New LockTools
Dim lockname As String
Dim result As Integer
Dim workspace As New NotesUIWorkspace
Dim session As New notessession
If source.EditMode = False Then
LockName = LockTool.isDocLockedStr(source.document)
If LockName <> "" And LockName <> session.CommonUserName Then
Msgbox("This document is currently locked by " + LockName + ".")
continue = False
Exit Sub
End If
Call locktool.lockdoc(source.Document)
End If
End Sub
Here’s the corresponding QueryClose of the document:
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
' if doc is in edit mode... try and delete the locks
Dim locktool As New locktools
If source.EditMode Then
Call LockTool.unLockDoc(source.document)
End If
End Sub
Script Library Code
'libLockTools:
Option Public
Option Declare
%INCLUDE "lsconst.lss"
' The only scenario this can't handle is if the user has Author but not "Create Documents" role.
Const LOCK_VIEW = "($$Lock)"
Const LOCK_FORM = "zzLock"
Const LOCK_USERFIELD = "username"
Const LOCK_UNIDFIELD = "unid"
Const LOCK_DELETEVAL = "~~DELETEME~~"
Class LockTools
Private lockView As NotesView
Private userCanCreate As Boolean
Private userCanDelete As Boolean
Private userAccessLevel As Long
Sub New()
Dim session As New NotesSession
If (session.CurrentDatabase.QueryAccessPrivileges(session.UserName) And DBACL_CREATE_DOCUMENTS) > 0 Then
userCanCreate = True
Else
userCanCreate = False
End If
If (session.CurrentDatabase.QueryAccessPrivileges(session.UserName) And DBACL_DELETE_DOCUMENTS) > 0 Then
userCanDelete = True
Else
userCanDelete = False
End If
userAccessLevel = session.CurrentDatabase.CurrentAccessLevel
End Sub
Function isDocLockedStr(doc As NotesDocument) As String
Call init()
Dim entry As NotesViewEntry
Set entry = lockView.GetEntryByKey(doc.UniversalID,True)
If entry Is Nothing Then
isDocLockedStr = ""
Else
isDocLockedStr = entry.ColumnValues(1) ' this code is up for debate. but this is fast and easy
End If
End Function
Function lockDoc(doc As NotesDocument) As Boolean
' debating this code. Do I return false if the user can't create docs?
If userCanCreate = False Then
LockDoc = True
Exit Function
End If
lockdoc = False
If isDocLockedStr(doc) = "" Then
Dim session As New notessession
Dim newLockDoc As NotesDocument
Dim authorsItem As NotesItem
Set newLockDoc = session.CurrentDatabase.CreateDocument
Set authorsItem = New NotesItem( newLockDoc,"DocAuthors", session.UserName,AUTHORS)
newLockDoc.form = LOCK_FORM
Call newLockDoc.ReplaceItemValue(LOCK_UNIDFIELD,doc.UniversalID)
Call newLockDoc.ReplaceItemValue(LOCK_USERFIELD,session.UserName)
newLockDoc.Save True,False
lockdoc = True
Print "Document Successfully Locked"
End If
End Function
Function unLockDoc(doc As NotesDocument) As Boolean
unLockDoc = False
Dim entries As NotesViewEntryCollection
Call init()
Set entries = lockView.GetAllEntriesByKey(doc.UniversalID)
If userCanDelete = True Then
entries.RemoveAll(True)
unLockDoc = True
Else
Call entries.StampAll(LOCK_UNIDFIELD,LOCK_DELETEVAL)
End If
Print "Document Successfully Unlocked"
End Function
Function unLockAllDocs As Boolean
unLockAllDocs = False
Dim entries As NotesViewEntryCollection
Call init()
Set entries = lockView.AllEntries
entries.RemoveAll(True)
unLockAllDocs = True
End Function
Sub init()
If lockView Is Nothing Then
Dim session As New notessession
Set lockView = session.CurrentDatabase.GetView(LOCK_VIEW)
If lockView Is Nothing Then
Error 9003, "Cannot find Lock View"
End If
End If
End Sub
End Class
Why not just have functions in a Script Library?
I could have put all of the functions you see in a Script Library. One thing I like about Classes is that they are distinct. I can take this Class and add it to any form without worrying if I’m overwriting the “unlockdoc” routine.
Also, what if we want to keep the locks in a different database? You could extend the class: Class AdvancedLockTools as LockTools and rewrite the init() function.
What’s next?
What’s next for this code? I want to try and come up with a way to get Authors who cannot create documents to work.
This is such a simple example of a class. I have a reporting tool that uses classes to represent “columns” of data. Each column type (Number, text, date, name, etc… ) extends from a base class. The power of classes is evident when I put columns in a list and loop through the columns and calling a function (e.g. getData() or getFormatting()) that is different in each column.
I hope to provide some of these complex examples in the future. Object Oriented coding can be used in almost all of the languages we use as Notes developers (JavaScript, Java, and LotusScript).
There are 4 Comments to "Object Oriented Example – Document Locking"
Re: the Authors who can’t create issue…
I think this is what you are after…
if you make the Form zzLock “Available to Public Users”, and have the DB ACL to allow this group to “Write Public Documents”, they will then be able to create the lock form. Then you can make the field $PublicAccess= 0 on saving on the document created with zzLock, to set correct read\ author access to document.
@Nick – I’ve always implemented this solution without building a “lock” form design element… but you’re right. If I really need to have authors/without the create role, your idea would work perfectly.
Hi Tom -
I like the class. I use a similar one extensively at one my clients. I’m intrigued about the idea of putting all the lock documents in a different application. If there was a single locking application, you could provide everyone with author & delete access, control read access to the documents, and not worry about what other data is the application.
Here’s some other things to consider from my experience:
1. Use Public Access for users without create access. I agree with Nick about using the Public Access field on the lock form. We have many application where users can update existing documents but not create new documents. Public access works great for these scenarios.
2. Include a bypass for the locking routine for local replicas. No need to lock the document if I’m the only one using the application.
3. Create a scheduled agent to delete the unlocked lock documents. If users don’t have delete access, the unlocked lock documents can end up being in the database for a long time.
4. Provide a mechanism for the users to unlock their orphaned locked lock documents. The Notes client occasionally crashes. The locked lock document becomes orphaned, and no one can edit the document. I like the users to be able to help themselves by unlocking their own orphaned locked lock documents.
5. Create a scheduled agent to delete the locked lock documents. Sometimes the users don’t want to help themselves or don’t remember how to unlock their own documents. I unlock all the locked lock documents overnight. When I’m not there to help them unlock it or if the user does not report the issue, it “fixes” itself overnight.
6. Include the date/time in the message to the user when the document they are trying to edit is locked. Sometimes it’s handy to know that Jim has been editing the document since 8:30 this morning, and he’s scheduled for meetings the rest of the day.
7. Add the locking routine to the QueryOpen event. Some users know how to open the document in edit mode without going to read mode first.
7. Put all the code in a subform. It’s a lot easier to add a subform to a form and have all the code in place, especially if you are adding it to an existing application. Plus, it keeps the form events specific to the form itself.
Michael,
Thanks for the suggestions. I do implement a lot of them by pulling information from the base class. This was really a basic example of the class and how the code is used in some events.
I really wanted to start with a base class and then head into things like polymorphism.