With Notes 6.0 there appears to be a bug with document locking in the scenario in which UserA is editing a document and UserB then attempts to edit the same document on another server while UserA is still editing the document. When this happens UserB will be able to open the document but it will remain in read mode while UserA is editing the document. When UserA has finished editing the document UserB can then place the document in edit mode. The first time this is attempted a warning message is issued that USerB must re-open the document from the parent view and the document remains in Read mode. If UserB ignores the message and again attempts to edit the document without reopening it from the parent view the document actually opens in edit mode. If UserB then closes the document without making any changes, a lock is placed on the document by UserB that is not released after the document is closed. This can lead to an accumulation of locked documents in the database.
To solve this scenario I have extended the .DominoFramework as follows:-
A property has been added to the DominoDocument class that allows the detection of a document that is locked by somebody else:-
Property Get IsLockedBySomeoneElse As Boolean IsLockedBySomeoneElse = iDocument.LockHolders(0) <> "" And Isnull(Arraygetindex(iDocument.Lockholders,Session.UserName)) End Property
A new PreventEditIfLocked method can then be invoked by the PostOpen event on the form. This adds additional code to the QueryModeChange event to trap the scenario in which a locked document is subsequently placed in edit mode:-
Sub PreventEditIfLocked(Source As NotesUIDocument) If Me.IsLockedBySomeoneElse Then On Event QueryModeChange From Source Call Reload End Sub
The Reload method that is now triggered by the OnQueryModeChange event then closes the document and re-opens the document. This is done via an agent as I was unable to find any other way of forcing the current document to be closed and the re-opened. It was also necessary to unlock the document at this stage as it appears this is where the forgotten lock is enabled.
Sub Reload(Source As NotesUIDocument, Continue As Variant) Dim Agent As NotesAgent On Error Resume Next Call Source.Document.UnLock Call Source.Close(True) Set Agent = DB.GetAgent("aReopenDocument") If Not Agent Is Nothing Then Call Agent.Run(iDocument.NoteID$) End Sub
And finally, the agent to open the document is as follows:-
Sub Initialize Dim Session As New NotesSession Dim UIW As NotesUIWorkspace Dim Agent As NotesAgent Dim Doc As NotesDocument If Session.IsOnServer Then Exit Sub Set Agent = Session.CurrentAgent Set Doc = Session.CurrentDatabase.GetDocumentByID(Agent.ParameterDocID$) If Doc Is Nothing Then Exit Sub Set UIW = New NotesUIworkspace Call UIW.EditDocument(True,Doc,,,False) End Sub
All of the above code will be included as part of the 0.4 beta of the .Domino Framework planned for this weekend.
|
Ratings
0
|
Today I had an application in development in which an LEI Activitiy was somehow creating a small number of duplicate records. Rather than try to figure out why LEI was creating duplicates I chose to simply create an agent to delete the duplicates. I have extended the .Domino Framework to add a RemoveDuplicates method for the DominoView class.
To Make this work:-
- Create a view that sorts documents by the "key" with the key being in a column (for partial keys concatenate the values).
- (Optional) add a second columns that sorts the documents in such a way that the duplicate to be retained is at the top (e.g. creation date)
- Invoke the method using an action button on the view, and agent (manual or scheduled).
The methods created takes a column number as a parameter for the column containing the key anbd returns a count of the number of duplicates removed.
'/** ' * Remove all duplicate entries in view by retaining the first entry listed for each duplicate value ' * ' * @param ColumnPos The column number to be used to identify the unique values ' * @return The number of documents deleted from the view ' */ Function RemoveDuplicates(ColumnPos As Integer) As Long Dim AllEntries As NotesViewEntryCollection Dim Doc As NotesViewEntry Dim LastDoc As NotesViewEntry Dim NextDoc As NotesViewEntry If iView Is Nothing Then Exit Function Set AllEntries = iView.AllEntries Set LastDoc = AllEntries.GetFirstEntry Set Doc = AllEntries.GetNthEntry(2) While Not Doc Is Nothing Set NextDoc = AllEntries.GetNextEntry(Doc) If Doc.ColumnValues(ColumnPos%) = LastDoc.ColumnValues(ColumnPos%) Then RemoveDuplicates = RemoveDuplicates + 1 Call Doc.Document.Remove(True) Else Set LastDoc = Doc End If Set Doc = NextDoc Wend End Function
View Action:
Sub Click(Source As Button) Dim Count As Long Count& = View.RemoveDuplicates(0) If Count& > 0 Then Msgbox Cstr(Count&) + " duplicate documents deleted",MB_OK+MB_ICONINFORMATION Else Msgbox "No duplicates found",MB_OK+MB_ICONINFORMATION End If End Sub
|
Ratings
0
|
This is an FYI for those currently in the process of migrating to Domino 8.0/8.0.1.....
One of my clients is migrating from 7.0 to 8.0.1. They have customized their mail template to include a view that displays documents not filed in any folders. This view uses the special view type "Shared, contains documents not in any folders". It was found that when a user's mail database was upgraded to the lated ODS (48), this view would no longer work. The view simply does not display inside the mail database and the Domino server reports error trying to process the view. For now we have held off on upgrading mail files to ODS 48 pending a solution to the issue.
|
Ratings
0
|
One of the projects I am presently working on involves enabling document locking for an application using the Document Locking feature introduced with Notes 6.0. I had done this before with Notes 6.0 and recall there are issues with documents sometimes remaining locked even after the user has finished editing the document. And sure enough, when we started UAT we got reports of document being locked even though no-one was still editing the document. As part of the process for resolving this I have added Document Locking as a new feature within the .Domino Framework.
A new view ($Locked) has been created as a way of clearly displaying those documents that are presenty locked (i.e. the $Writers fields is present).
I have added a view action to remove the lock by deleteing the $Writers and $WritersDate field from the document . This allows people without Manager access to maintain the locks.
Testing done so far has shown that when the lock fails to be removed, the $writers field is present but the $WritersDate field is not present. I have therefore added a RemoveInvalidLocks method to the DominoDatabase class that will remove any document in the ($Locked) view for which this situation arises. By creating an agent the runs periodically against the database invoking this method it is possible to locate and correct these issues as they occur.
Note: Further testing is still required to ensure that only invalid locks are being removed and that this technique is identifying all the document locks that persist after they are no longer needed....
|
Ratings
0
|
Today I was testing an application to make sure that changes made to ensure replication merged changes at the field level rather than the default document level. To do this we set up replicas on two different dev servers. Because the two servers do not normally repolicate with each other (and because we didn't want to wait that long) I decided to replicate the databases manually from my workstation. When I did, I found that the documents did not merge changes at the field level but at the document level. My initial thoughts were that there was a problem with the field level merging. Later we figured out a way to get the servers to do the replication. When the servers initiated the replication the field level merging took place as intended.
It would appear that replication that is initiated from the Notes client (Notes 6.0) does not perform field level replication. I need to do some further tests to confirm this, but obvioulsy I need to be careful when I use my workstation to replicate databases as this may very well result in a different outcome for applications designed to merge changes at the field level... Yikes
|
Ratings
0
|
On of my challenges for last week was to find an easy way to add an RSS feed to a Notes application. After doing some research on the topic I found that the XML required for an RSS feed is fairly simple and there were already a number of excellent examples out there within the Notes community. In the spirit of the .Domino Framework I wanted a generic solution which could easily applied to multiple applications. In the end I based some of my my code on the OpenNTF Blogsphere (thanks Declan).
I have created a new DominoRSS Feed class. This is intented to be part of a new Domino.Applications.RSS namespace but I have been having technical problems getting the same code to run as a Web agent when placed in a LotusScript library. So, for now, the class definition is included as part of the agent.
A new agent has been developed with an alias of content.rss that invokes various methods/properties in the class to generate the necessary XML. The code for the agent is as follows:-
Sub Initialize Dim RSSEngine As DominoRSSFeed Set RSSEngine = New DominoRSSFeed(Nothing) RSSEngine.NotesLinks = True Call RSSEngine.GenerateHeader Call RSSEngine.GenerateViewContent("($RSS)",0) Call RSSEngine.GenerateFooter Call RSSEngine.WriteXML End Sub
- The NotesLinks property is used to determine if the RSS Feed should publish links using http protocol (default) or Notes protocol. In my case I wanted the documents to launch in the Notes client as the source application is not Web-enabled.
- The GenerateHeader method creates the initial XML for the feed/channel
- The GeneratViewContent method creates the XML for the feed based upon the contents of the provided view. The view can be any view with the programatic column name for each column used to create XML elements. Use any of the available RSS feed attributes such as "title", "description","author","pubDate" to map the view content to the RSS feed.
- The GenerateFooter method creates the closing XML
- The WriteXML method takes the XML held internally within a NotesStream and writes it back to the screen.
I can now make any application RSS enabled by simply adding the agent (with the DominoRSSFeed Class) and having a view that meets the requirements above.
Note: The code for the class will be published as part of the next beta of the .Domino Framework later this month. .Domino Framework wiki will provide documentation on the RSS Feed XML. I will also be working on an enhancement to define various RSS attributes (e.g. Description) as part of the Application Settings.
|
Ratings
0
|
The following property has been added to the DominoDateTime class to allow a date/time value to be represented in a format consistent with ISO 8601's Universal Time (UTC). This will be available as part of beta 0.4 later coming this month.
'/** ' * ISO Coordinated Universal Time (UTC) format for date ' */ Property Get UTC As String Dim UTCDayName As String Dim UTCDay As String Dim UTCMonth As String Dim UTCYear As String Dim UTCTime As String Dim UTCSign As String Dim UTCZone As String If iDate Is Nothing Then Exit Property UTCDayName$ = Format$(iDate.LSLocalTime, "ddd") UTCDay$= Format$(iDate.LSLocalTime, "dd") UTCMonth$ = Format$(iDate.LSLocalTime, "mmm") UTCYear$ = Format$(iDate.LSLocalTime, "yyyy") UTCTime$ = Format$(iDate.LSLocalTime, "hh:nn:ss") If Sgn(iDate.TimeZone) < 0 Then UTCSign$ = "-" Else UTCSign$ = "+" If iDate.TimeZone < 10 Then UTCZone$ = "0" + Cstr(Abs(iDate.TimeZone)) Else UTCZone$ = Cstr(Abs(iDate.TimeZone)) UTCZone$ = UTCSign$ + UTCZone$ + "00" UTC$ = UTCDayName$ + ", " + UTCDay$ + " " +UTCMonth$+ " " +UTCYear$ + " " +UTCTime$ + " " + UTCZone$ End Property
|
Ratings
0
|
Have you ever wished it was possible to program an event such as QuerySave in both @Language and LotusScript? Sometimes the most efficient (or only) way to perform a specific task is to code the event using @Formula and @Commands. That works fine until a request comes along in which LotusScript is the only solution. Using Evaluate within LotusScript can sometimes be a solution but there is another way to achieve this.
1) Create a class
2) In the constructor for the class create an event handler for the event and set it to invoke a sub of that class
3) Create the method using the same signature as the event with the LotusScript code and put all the LotusScript code in there
4) In the QueryOpen event create an instance of the class passing the NotesUI object (this can be any event invoked prior to the called event - QueryOpen is usually the first and is usually the best)
5) Place the @Language component in the actual event on the form/page.view
When the event is fired the @Language coded in the the actual event will be executed and then the eventhandler coded in the class will be executed....
Form example:
Class MyClass
Sub New(Source As NotuesUIDocument)
On Event QuerySave From Source Call OnQuerySaave
End Sub
Sub OnQuerySave(Source as NotesUIDocument,Continue As Variant)
' Place all LS QuerySave code here
End Sub
End Class
Dim Var As MyClass
Sub QueryOpen(Source As NotesUIDocument)
Set Var = New MyClass(Source)
End Sub
|
Ratings
0
|
A common feature request I get for applications is the ability to adjust dates so that they do not fall on a weekend or a business holiday. Whilst there is no @BusinessDay function, I often wish there is one. The .Domino Framework now provides a LotusScript equivalence for what I envisage an @BusinessDay function should/could look like. It takes a date or array of dates (of LS type DATE or NOTESDATETIME), and array of non-worrking days and adjusts the dates so that none of them fall on a weekend or holiday. This is available in the Domino.AFunctions namespace.
The code is as follows:-
/** ' * Returns a list of business days for a list of dates, adjusting those dates that fall on a weekend or holiday ' * ' * @author Peter Presnell ' * @param Source A list of one or more dates ' * @param Holidays A list of dates that are considered non-working dates ' * @param MoveAfter True = move date to next working day, False = move date to previous working day ' * @return A list containing the dates adjusted for weekends and holidays ' */ Function atfBusinessDay(Source As Variant, Holidays As Variant,MoveAfter As Boolean) As Variant Dim Index As Integer ' Counter used to loop through arrays Dim Results As Variant ' Interim results of atfBusinessdays Dim DateTime As Variant Try: On Error Goto Catch ' atfBusinessDay = Source ' If array of dates passed then caluclate business days for each pair of dates If Isarray(Source) Then For Index% = Lbound(Source) To Ubound(Source) Redim Preserve Result(Index%) Result(Index%) = atfBusinessDay(Source(Index%),Holidays,MoveAfter) Next Index% atfBusinessDay = Results Else Select Case Typename(Source) Case "DATE" While Weekday(Source) = 1 Or Weekday(Source) = 7 Or atfIsMember(Source,Holidays) If MoveAfter Then Source = Source + 1 Else Source = Source - 1 Wend atfBusinessday = Source Case "NOTESDATETIME" DateTime = Cdat(Source.DateOnly) While Weekday(DateTime) = 1 Or Weekday(DateTime) = 7 Or atfIsMember(DateTime,Holidays) If MoveAfter Then DateTime = DateTime + 1 Else DateTime = DateTime - 1 Wend Set atfBusinessDay = New NotesDateTime(DateTime) End Select End If Exit Function Catch: Stop Call ReportError Exit Function End Function
|
Ratings
0
|
From time to time I have found a need to perform the equivalence of @DBLookup within LotusScript. The following is the code I have just finished revising which will implement @DBLookup using LotuScript code only (i.e. withount an evaluate). This forms part of an effort to provide LotusScript euqivalnts for most @Functions without using Evaluate for those times when evaluate doesn't work opr to expand the functionality beyond that provided by teh basec @function... This updated function will be included in the next beta release of the .Domino Framework as part of the Domino.@Functions namespace.
Note: In most cases Evaluate works just fine and will often produce a much faster result .
'/** ' * Given a key value, looks in the specified view (or folder) and finds all documents containing the key value in ' * the first sorted column within the view. For each selected document, @DbLookup returns either the contents ' * of a specified column in the view, or the contents of a specified field. ' * ' * @author Peter Presnell ' * @param Database NotesDatabase object, server/filepath, or replicaid id of database ' * @param View NotesView or name of view ' * @Param Key Value used to locate entries in view ' * @param Column Column number or name of field corresponding to values to be returned ' * @return List of matched values ' */ Function atfDBLookup(Database As Variant, View As Variant,Key As Variant, Column As Variant, FailSilent As Boolean) As Variant Dim iDB As New NotesDatabase("","") ' Database to perfrom lookup Dim iView As NotesView ' View to perfrom lookup Dim Doc As NotesDocument ' Document that corresponds to current entry in view Dim ViewEntries As NotesViewEntryCollection Dim ViewEntry As NotesViewEntry Dim ColumnValues As Variant Dim Results As Variant ' Interim results Dim Index As Long ' Counter used to loop through array Try: On Error Goto Catch atfDBLookup = EmptyArray Redim Results(0) Select Case Typename(View) Case "NOTESVIEW" Set iView = View Set iDB = iView.Parent Case "OBJECT" Set iDB = Session.CurrentDatabase Case "STRING" If Isobject(Database) Then Select Case Typename(Database) Case "NOTESDATABASE": Set iDB = Database End Select Elseif Isscalar(Database) Then Call iDB.OpenByReplicaID(Session.CurrentDatabase.Server,Cstr(Database)) Elseif Isarray(Database) Then If Database(1) = "" Then |