Easy updates for your Notes applications

I don’t like the whole template and design update/replace handling with Notes Applications. It’s ok when you have access to the server and application you want to update, but usually I don’t have such access. I’m developing for customers and many of them have no deep Notes knowledge. So in most of the cases, when I’m sending out new versions of applications, I get an answer like this: “Can we install the update together in a Teamviewer session?”.

This is not only the case for customers, but also for all these Notes to Exchange projects currently happening… I’m developing some tools for my colleagues working on those projects, but they usually don’t have much Notes know-how and I always have to update these applications by myself or in a remote session.

To get all these updates done more easily, I thought about a little update function for my applications. It should be easier to use than the standard process. Finally, yesterday I found a little bit of time to implement this. It is the first version, so there is still a lot of room for improvements and I also have a lot of more ideas for the future.

For the moment, that’s the code. One Agent and one script library. You just have to add them to your application to get the update function running. Agents won’t get updated to preserve settings and signatures. When executed using the actions menu, it asks for the source database, deletes all design elements (ATTENTION!!) of the current database except agents. At last it copies all design elements from the selected source database to the current one and your application should be updated to the new version.

Any recommendations, bug reports and contributions are welcome!

As always, use the code with caution and at your own risk.

Agent “Version\Update”

%REM
 Agent Version\Update
 Created Mar 30, 2016 by Stephan Kopp
%END REM

Option Public
Option Declare
Use "stdlib_update"

Sub Initialize
 Dim l_update As New DBUpdate
 Call l_update.funcUIStartUpdate()
End Sub

Script Library “stdlib_update”

%REM
 Library stdlib_update
 Created Mar 30, 2016 by Stephan Kopp
%END REM

Option Public
Option Declare

Public Const lsERR_NOTES_DBNOACCESS = 4060 
Public Const lsERR_NOTES_DATABASE_NOTOPEN = 4063

%REM
 <b>Class DBUpdate</b>

 <u>Created:</u>
 Mar 30, 2016 by Fritz & Macziol
 <u>Contributors:</u>
 Stephan Kopp, Fritz & Macziol
 <u>Description:</u>
 
%END REM
Public Class DBUpdate
 private m_source As NotesDatabase
 Private m_target As NotesDatabase
 Private m_session As NotesSession
 Private m_uiw As NotesUIWorkspace
 Private m_thisdb As NotesDatabase

 Sub New
   Set me.m_uiw = New NotesUIWorkspace
   Set me.m_session = New NotesSession
   Set me.m_thisdb = me.m_session.Currentdatabase
 
   'at the moment, target is always the current database
   Call me.funcSetTarget(me.m_thisdb.Server, me.m_thisdb.Filepath)
 End Sub
 
 %REM
 <b>Function funcSelectSource</b>
 <p><u>Description:</u>
 Ask for the source database and save it for further processing.
 </p>
 %ENDREM
 private Function funcUISelectSource
   On Error GoTo catch
   funcUISelectSource = False
 
   Dim aw As Variant
   aw = m_uiw.Prompt(13, "Select your source template", "Select the source database or template to use for update")
   If IsEmpty(aw) Then GoTo done
 
   funcUISelectSource = me.funcSetSource(aw(0), aw(1))
 
   If not funcUISelectSource Then
     MessageBox "Can't open your selected database, please check log for details"
   End If

  done:
   Exit Function
  catch:
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
 %REM
 <b>Function funcSetSource</b>
 <p><u>Description:</u>
 Set the source database used for the update procedure.
 </p>
 <p><u>Return value:</u>
 true if sucessfully opened the source database
 </p>
 %ENDREM
 private function funcSetSource(p_server As String, p_file As string) As boolean
   On Error GoTo catch
   On Error lsERR_NOTES_DBNOACCESS GoTo noAccess
   On Error lsERR_NOTES_DATABASE_NOTOPEN GoTo noAccess
 
   Set me.m_source = me.m_session.Getdatabase(p_server, p_file)
   If me.m_source.Isopen Then
     funcSetSource = true
   Else
     funcSetSource = false
   End If

  done:
   Exit Function
  catch:
   print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
  noAccess:
   funcSetSource = False
   Print "ERROR, Can't open database: " & p_server & "!!" & p_file
   GoTo done
 End Function
 
 %REM
 <b>Function funcSetTarget</b>
 <p><u>Description:</u>
 Set the target database to update with the source databse.
 ATTENTION! All design elements of this database will be overwritten!
 </p>
 <p><u>Return value:</u>
 true if sucessfully opened the target database
 </p>
 %ENDREM
 Private Function funcSetTarget(p_server As String, p_file As String) As Boolean
   On Error GoTo catch
   On Error lsERR_NOTES_DBNOACCESS GoTo noAccess
   On Error lsERR_NOTES_DATABASE_NOTOPEN GoTo noAccess

   Set me.m_target = me.m_session.Getdatabase(p_server, p_file)
   If me.m_target.Isopen Then
     funcSetTarget = True
   Else
     funcSetTarget = False
   End If

  done:
   Exit Function
  catch:
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
  noAccess:
   funcSetTarget = False
   Print "ERROR, Can't open database: " & p_server & "!!" & p_file
   GoTo done
 End Function
 
 %REM
 <b>Function funcStartUpdate</b>
 %ENDREM
 Public Function funcUIStartUpdate As Boolean
   On Error GoTo catch

   If not me.funcUISelectSource() Then
     'exit
     GoTo done
   End If

   'show message to the user with details and ask again if update should be started
   Dim aw As Variant
   aw = m_uiw.Prompt(PROMPT_YESNO, "Are you sure?", "Do you really want to start the update?" & Chr(13)_
 & "All design elements (except agents) in the target database will be overwritten!" & Chr(13)_
 & "Source: " & me.m_source.Server & "!!" & me.m_source.Filepath & Chr(13)_
 & "Target: " & me.m_target.Server & "!!" & me.m_target.Filepath)
    If aw <> 1 Then GoTo done

   'maybe later we can add an option mask to select some types of design elements to update
   'e.g. All, Script Libraries only, etc.

   'start update
   funcUIStartUpdate = me.funcStartUpdate()
   If funcUIStartUpdate Then
     Messagebox "Update successfull, please restart your Notes Client"
   Else
     MessageBox "Something went wrong, please try again or contact Stephan Kopp"
   End If

  done:
   Exit Function
  catch:
   funcUIStartUpdate = false
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
 %REM
 <b>Function funcStartUpdate</b>
 <p><u>Description:</u>
 Start the update process
 </p>
 %ENDREM
 Private Function funcStartUpdate As Boolean
   On Error GoTo catch
   funcStartUpdate = False
 
   'delete design elements
   funcStartUpdate = me.funcDeleteAllDesignElements()
   If funcStartUpdate = False Then GoTo done
 
   'copy design elements
   funcStartUpdate = me.funcCopyAllDesignElements()

   'do some checks?
   'send notify?
   'sign database?

  done:
   'print message
   If funcStartUpdate Then
     Print "Update successfull, please restart your Notes Client"
   Else
     Print "Something went wrong, please try again or contact Stephan Kopp"
   End If
   Exit Function
  catch:
   funcStartUpdate = false
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
 %REM
 <b>Function funcDeleteAllDesignElements</b>
 <p><u>Description:</u>
 Deletes all design elements in selected target database except agents.
 Agents won't be updated to preserve scheduling and signatures.
 </p>
 %ENDREM
 Private Function funcDeleteAllDesignElements As Boolean
   On Error GoTo catch
   funcDeleteAllDesignElements = False
 
   'create a note collection with all design elements except agents
   Dim l_nodecol As NotesNoteCollection
   Set l_nodecol = funcCreateNoteCollection(me.m_target)
   Print "Found " & CStr(l_nodecol.Count) & " design elements to delete..."

   Dim l_count As Integer
   Dim l_nodeid As String
   l_nodeid = l_nodecol.Getfirstnoteid()
   While l_nodeid <> ""
     l_count = l_count+1
 
     Dim l_doc As NotesDocument
     Set l_doc = me.m_target.Getdocumentbyid(l_nodeid)
     If not l_doc Is Nothing Then
       Print "deleting element " & CStr(l_count) & " of " & CStr(l_nodecol.Count)
       Call l_doc.Remove(true)
     End If
 
     l_nodeid = l_nodecol.Getnextnoteid(l_nodeid)
   Wend

   funcDeleteAllDesignElements = true
  done:
   Exit Function
  catch:
   funcDeleteAllDesignElements = False
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
 %REM
 <b>Function funcCreateNoteCollection</b>
 <p><u>Description:</u>
 Creates a notecollection used for delete and copy of design elements.
 </p>
 %ENDREM
 Private Function funcCreateNoteCollection(p_db As NotesDatabase) As NotesNoteCollection
   On Error GoTo catch

   Dim l_nodecol As NotesNoteCollection
   Set l_nodecol = p_db.Createnotecollection(True) 'select all
   l_nodecol.Selectacl = False
   l_nodecol.Selectagents = False
   l_nodecol.Selectdocuments = False
   l_nodecol.Selectprofiles = False
   Call l_nodecol.Selectalldatanotes(False)
   Call l_nodecol.Selectalladminnotes(False)
   Call l_nodecol.Buildcollection()

   Set funcCreateNoteCollection = l_nodecol
  done:
   Exit Function
  catch:
   Set funcCreateNoteCollection = nothing
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
 %REM
 <b>Function funcCopyAllDesignElements</b>
 <p><u>Description:</u>
 Copies all design elements from selected source db to target db except agents.
 Agents won't be updated to preserve scheduling and signatures.
 </p>
 %ENDREM
 Private Function funcCopyAllDesignElements As Boolean
   On Error GoTo catch
   On Error GoTo catch
   funcCopyAllDesignElements = False
 
   'create a note collection with all design elements except agents
   Dim l_nodecol As NotesNoteCollection
   Set l_nodecol = funcCreateNoteCollection(me.m_source)
   Print "Found " & CStr(l_nodecol.Count) & " design elements to copy..."

   Dim l_count As Integer
   Dim l_nodeid As String
   l_nodeid = l_nodecol.Getfirstnoteid()
   While l_nodeid <> ""
     l_count = l_count+1
 
     Dim l_doc As NotesDocument
     Set l_doc = me.m_source.Getdocumentbyid(l_nodeid)
     If Not l_doc Is Nothing Then
       Print "copy element " & CStr(l_count) & " of " & CStr(l_nodecol.Count)
       Call l_doc.Copytodatabase(me.m_target)
     End If
 
     l_nodeid = l_nodecol.Getnextnoteid(l_nodeid)
   Wend

   funcCopyAllDesignElements = True
  done:
   Exit Function
  catch:
   funcCopyAllDesignElements = false
   Print "ERROR: " & Error & " (" & CStr(Err) & " / Line: " & CStr(Erl) & ")"
   Exit Function
 End Function
 
End Class

Ideas for the future:

  • Update also Notes Agents, but save and restore all scheduling and other settings
  • Download updates from a hosted web service automatically
  • Download and apply updates automatically by a server agent in background
Advertisements
This entry was posted in IBM Notes/Domino and tagged , , , , , . Bookmark the permalink.

One Response to Easy updates for your Notes applications

  1. Alexander B. says:

    I’m using a batch file to update the design:
    REN C:\LOTUS\DOMINO\DATA\template.ntf template.ntf.20160331
    COPY template.ntf C:\LOTUS\DOMINO\DATA
    C:\LOTUS\DOMINO\NDESIGN.EXE -f application.nsf
    ECHO “Update successfully completed”

    … and a bit modified one to downgrade in case of unexpected errors:
    REN C:\LOTUS\DOMINO\DATA\template.ntf template.ntf.20160331-fail
    REN C:\LOTUS\DOMINO\DATA\template.ntf.20160331 template.ntf
    C:\LOTUS\DOMINO\NDESIGN.EXE -f application.nsf
    ECHO “Downgrade successfully completed”

Comments are closed.