Problem Description

In this article I will show you how to elegantly link documents in a Notes database with the use of event handlers. At first glance linking documents together seems to be a very trivial task. It is something we all implemented hundreds of times already, either using built-in Notes response hierarchies or through building document trees of our own design. Doing that programmatically while using backend classes is as trivial as it sounds, but letting the user do it interactively is something else entirely. Because then there are certain additional requirements that should be implemented to guarantee a consistent and hassle-free user experience.

So let us take a look at those requirements for the two standard use cases:

1. The user links an already existing document to the currently open document (through a dialog field or a view selection). The title of the linked document should be displayed in a field and the document can be opened through a hotspot.


  • If the user doesn’t save the currently open document the link should be voided i.e. the field should be empty and the hotspot action should not execute.
  • The operation should not access backend classes to avoid replication and safe conflicts.

2. The user creates a new document from an already open document. The new document should be linked to the already opened one and its title should be displayed in a field (and can, after the initial save, opened through a hotspot).


  • If the user doesn’t safe the new document AND the current document the link should be voided i.e. the field should be empty and the hotspot action should not execute, even if the new document has been saved.
  • The operation should not access backend classes to avoid replication and safe conflicts.
  • Every change the user performs on the new document should immediately be reflected in the old document.



During the following demonstration I will call the document that provides the link the parent and the link target the child. I also will not make use of the Parent-Response-Hierarchy but will implement my own hierarchy using document IDs I generate myself programmatically. I am sure you know why that is . Of course you can use a Parent-Response Hierarchy instead and, if you are feeling lucky, you can even use Universal IDs to define the link.


Case 1: Linking to an existing document

The first case is more or less trivial. The child already exists and will not be opened during the linking operation hence we actually can (have to actually) access it using backend classes, specifically an instance of NotesDocument or built-in functions e.g. a dialog list field.
We create a dialog list field on the parent form and populate the choices with a column of a lookup view. The column formula should be something like the following:

doc_title + “|” + doc_id

Now make sure “Allow keyword synonyms” is checked in the field properties and you are done.

If you have to actually use backend classes, for example in conjunction with the NotesUIWorkspace Functions PicklistCollelction or PicklistStrings you can use the NotesUIDocument Function FieldSetText to set the value. And yes you can set computed and even hidden fields that way, too. I normally use a dialog list field even if the field is computed and set with FieldSetText. That way I can set the doc_id and after a NotesUIDocument.Refresh the displayed value is automatically translated to the title. For this to work you of course have to check the option “Refresh choices on document refresh” in the field properties.

The hotspot action performs a lookup in the same view (the first sorted column has obviously to be doc_id) and opens the document.


Case 2: Linking a new document

This case requires a little more work. So let us split the requirements into two steps to make it clearer.

For the purpose of code clarity and management we create persistent backend classes which serve as a wrapper for NotesUIDocument instances. We accomplish that through creating an instance of our wrapper in the PostOpen event and saving the reference in a variable declared in the Globals section of the form. For more details read my previous articles on the subject:

Event Handling mit LotusScript Teil I - Persistente Event-Handler


Step 1: Creating the child

In the backend class for the parent we first declare a private member variable:

Private childUIDoc As NotesUIDocument


And the following sub:

Public Sub actionCreateChild()

   Dim ws As New NotesUIWorkspace()
   Dim db As NotesDatabase
   Dim childDoc As NotesDocument

   Set db = ws.CurrentDatabase.Database
   Set childDoc = db.CreateDocument()

   Call childDoc.ReplaceItemValue( "Form", "childDoc" )
   Call childDoc.ComputeWithForm( False, False )

   Set me.childUIDoc = ws.EditDocument( True, childDoc )

End Sub


That sub will be called by the action which we use to create the child document. In effect our persistent instance of the parent wrapper class now has a handle on the NotesUIDocument instance of the just created child.

Step 2: Handling Child Events in the Parent-Wrapper

Next we want to notify the parent if and when the child gets saved or closed. For that purpose we bind the corresponding events, which are fired by the child NotesUIDocument instance, to event handlers we define in our parent wrapper.

Public Sub actionCreateChild()
    Dim ws As New NotesUIWorkspace()
    Dim db As NotesDatabase
    Dim childDoc As NotesDocument
    Set db = ws.CurrentDatabase.Database
    Set childDoc = db.CreateDocument()
    Call childDoc.ReplaceItemValue( "Form", "childDoc" )
    Call childDoc.ComputeWithForm( False, False )
    Set me.childUIDoc = ws.EditDocument( True, childDoc )

    On Event PostSave From me.childUIDoc Call OnPostSave_Child
    On Event QueryClose From me.childUIDoc Call OnQueryClose_Child

End Sub


All the PostSave event handler has to do is to set the doc_id and refresh the NotesUIDocument instance of the parent document. This way the current title of the document is always displayed.

Public Sub OnPostSave_Child( Source As NotesUIDocument )
    Call me.mUIDoc.FieldSetText( "child_id", _
                                  Source.FieldGetText( "doc_id" ) )
    Call me.mUIDoc.Refresh()
End Sub


In the QueryClose event handler we first check if the child has been saved. Since we created the document using backend classes we need to check the property IsNewNote on the backend document rather than the property IsNewDoc which is provided by the NotesUIDocument instance (and hence will always be false). If the child has been saved we do nothing, everything already has been taken care of by the PostSave event handler. If it has not been saved we reset the link field to the empty string, thus removing the link.

Public Sub OnQueryClose_Child( Source As NotesUIDocument, _
                               Continue As Variant )
    If childUIDoc.Document.IsNewNote Then
          Call me.mUIDoc.FieldSetText( "child_id", "" )
          Call me.mUIDoc.Refresh()
    End If
End Sub


And that is it. If the child doesn’t get saved the link will be empty. If the parent doesn’t get saved the link won’t change at all. If it was empty before the child was created it will obviously stay that way regardless whether or not the child has been saved.


Step 3: Opening the link

To keep the behavior consistent we also have to obtain the child NotesUIDocument instance when the user follows the link. For that we create a new sub for the sole purpose of opening the document.

Public Sub actionOpenChild()

    Dim ws As New NotesUIWorkspace()
    Dim db As NotesDatabase
    Dim view As NotesView
    Dim childDoc As NotesDocument
    Dim childID As String
    childID = me.mUIDoc.Document.GetItemValue( "child_id" )(0)
    If Not childID = ""  Then
          Set db = ws.CurrentDatabase.Database()
          Set    view = db.GetView( "lkpChildDoc" )
          Set childDoc = view.GetDocumentByKey( childID, True )
          If Not childDoc Is Nothing Then
                Set me.childUIDoc = ws.EditDocument( True, childDoc )
          End If
    End If
End Sub


So in theory we are done but what if the child gets opened end edited outside of the parent context? Well if we use the dialog list approach we are still done because when the child gets edited the view entry will change and the link title will be updated the next time you open the parent (into edit mode). But what if you want the parent to reflect changes in the child immediately?
There probably is a reason why you made one document the child and the other the parent and not the other way around. You built your hierarchy to reflect some piece of business logic or maybe even real logic .

In consequence it is highly likely that the user is already editing the parent when he opens the child. Hence updating the parent programmatically through the child without having the parent context invites all kinds of trouble. So don’t! Rather write an agent that runs once a night and performs a ComputeWithForm on all parent documents.



Normally, updating RichTextFields in an opened IBM Notes document requires saving, closing and then reopening the document. If you use the RichTextField to attach a file to the document even after doing all this closing and reopening, it is not guaranteed that the attachment is inserted where you want it to be. Another problem that is implicated with this is the necessity to save documents you do not want to save, yet.

Fortunately, there is an undocumented method in LotusScript that enables us to update RichTextFields on unsaved UIDocuments without the need for saving and reopening them.

It's called

NotesUIDocument.ImportItem( tmpDoc As NotesDocument, RichTextItem As NotesRichTextItem)

and it is part of in the NotesUIDocument class.


Explanation standard method:

The standard method to embed files or document links etc. into RichTextFields is the use of



Example Code:

    Dim ws As New NotesUIWorkspace
    Dim db As NotesDatabase
    Dim doc As NotesDocument
    Dim rtitem As NotesRichTextItem
    Set doc = ws.CurrentDocument.Document
    Set rtitem = New NotesRichTextItem(doc, "RichTextField")
    Call rtitem.EmbedObject( EMBED_ATTACHMENT, "", "C:\Desktop\Document.docx")


Fig. 1: Notes form with buttons for the standard method and our workaround for attaching files in the RichTextField


After using the standard method the problems stated above become obvious. When the document is saved and reopened the file is attached at the end of the document, but not in the RichTextField where it should be.

Fig. 2: Result of the standard method EmbedObject of LotusScript


Explanation workaround:

Now we will use the ImportItem method.


Example Code:

    Dim sess As New NotesSession
    Dim ws As New NotesUIWorkspace
    Dim db As NotesDatabase
    Dim tmpdoc As NotesDocument
    Dim rtItem As NotesRichTextItem
    Dim uiDoc As NotesUIDocument
    'uiDoc contains the RichTextField we want to import our file into             
    Set uiDoc = ws.CurrentDocument
    'Create a support NotesDocument (tmpDoc)       
    Set db = sess.currentDatabase
    Set tmpdoc = db.CreateDocument
    'Create a RichTextItem on the support document to embed the desired file into
    Set rtItem = New NotesRichTextItem(tmpdoc, "RichTextField")
    'embed the desired file into the created RichTextItem
    Call rtItem.EmbedObject( EMBED_ATTACHMENT, "", "C:\Desktop\Document.docx")
    Call rtItem.update
    '!!Using computeWithForm ensures that the attachment is completely embed into the RichTextField before
    'we try to import it into our uiDoc. It works even though we have no form specified!!

    Call tmpdoc.ComputeWithForm(False, False)
    'Put Cursor into the RichTextField --> precondition for the file's import
    Call uiDoc.GoToField("RichTextField")
    'finally import the desired file into the RichTextField on the uiDoc.
    Call uiDoc.ImportItem(tmpDoc, "RichTextField")


Fig. 3: Result of the workaroung method ImportItem


As you can see, right after using the workaround button, the file is attached into the RichTextField of our opened document as desired. No saving and reopening needed!

!Please keep in mind that this workaround method is undocumented an could be removed from LotusScript by IBM!
I hope this workaround helped you as much as it helped me!
If you have any questions or suggestions do not hesitate to contact us.


Today I found a strange sideeffect in LotusScript. After calling the method


I used the @Function


some times later in my IBM Notes Standard Client. The result from this @Formula was "Web" <- which is obviously not true ;)

Only after restarting the Notes Client @ClientType seems to be 'resetted'. I tested this behaviour on Notes 8.5.3 FP6 and Notes 9.0.1 with the same effect. I realized times ago that @ClientType does not work correctly, but I didn´t see a pattern. Now I could explain why sometimes the formula results the wrong value.

I just opened a PMR at IBM, perhaps there is someone who could tell me in the meantime if there is a possibility to 'reset' the @ClientType to normal after using NotesDocument.convertToMIME() ;) (notes.ini, other script function or so)

Many thanks!


Here is a little code for testing. Just paste it in a view action or button:

        Dim ws As New NotesUIWorkspace
        Dim session As New NotesSession
        Dim db As NotesDatabase        
        Dim doc As NotesDocument
        Dim vRes As Variant
        vRes =  Evaluate({@ClientType})
        Print "Client Type before: " & Cstr(vRes(0))
        Set db = session.CurrentDatabase
        Set doc = db.CreateDocument
        Call doc.ConvertToMIME()
        vRes =  Evaluate({@ClientType})
        Print "Client Type after: " & Cstr(vRes(0))



Uns stellte sich schon das ein oder andere Mal die Frage, ob es möglich sei, die Programmlogik für XPages und Legacy Anwendungen gemeinsam zu pflegen und zu nutzen. Hierzu drängte sich uns direkt LS2J von IBM auf, ein Framework, das es ermöglicht, aus LotusScript heraus direkt Java-Klassen zu benutzen.

Hier bekommt ihr einen kurzen Überblick zu LS2J:


Grundsätzlich ist es recht einfach zu benutzen: Als erstes legt man sich eine Java-Klasse an z.B.:

public class myTestClass {

  public void doSomething() {

   System.out.println("Hallo Welt");




Als nächstes legt man sich ein wenig LotusScript Code an

Option Declare

 UseLSX "*javacon"

 Use "TestJavaLib"

Sub useJavaClass

      Dim js As New JavaSession

      Dim myClass As JavaClass

      Dim myObject As JavaObject

      Set myClass = js.GetClass("myTestClass")

      Set myObject = myClass.CreateObject

      Call myObject.doSomething()

End Sub


Nun fügt man den LotusScript-Teil z.B. in einen Button auf einer Form ein:



Durch das Benutzen des Buttons erhält man nun in der Java-Konsole folgende Ausgabe:


Wer ein weiterführendes Beispiel benötigt, wird hier fündig:


Leider ist es nur möglich, primitive Datentypen wie int, double, String, usw. zu übergeben - keine Objekte. Und das bringt uns leider zu unserem ersten Problem:

„Wie bekommen wir es hin, ein Notesdokument an eine Java Klasse zu übergeben?“

Jetzt wird jeder sagen „Ganz einfach: Übergib doch die Note ID und hol dir das Dokument aus der Datenbank“. Den Gedankengang hatten wir auch, leider hat sich hieraus ein neues, viel schwerwiegenderes Problem ergeben:

Versucht man, über die bekannten Wege an eine Session zu kommen, wird man kläglich scheitern.


Hier mal die Wege, die wir getestet haben:

Versuch 1:

  public class GetSessionV1 {

      public GetSessionV1() {


            Session session = NotesFactory.createSession();



Resultat: "NotesException: Cannot create a session from an agent"


Versuch 2:

  public class GetSessionV2 extends AgentBase{

      public GetSessionV2() {

            Session s= getSession();



Resultat: "Session ist null"


Den Weg über die DominoUtils haben wir außen vor gelassen, da wir ja nicht auf einer XPage unterwegs sind. Da wir also bis jetzt leider keine Möglichkeit gefunden haben, aus Java heraus das Lotus Notes Backend anzusprechen, ist unsere Idee erstmal auf Eis gelegt. Schade eigentlich.

Vielleicht habt ihr ja eine Idee zu dem Thema?