Falling Dominos | Let's keep Lotus Notes development relevant

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).

Comment Pages

There are 4 Comments to "Object Oriented Example – Document Locking"

  • Nick Wall says:

    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.

  • Tom ONeil says:

    @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.

  • Tom ONeil says:

    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.

 

Essentials