All about Lotus Domino Development (AaLDD)

Performance basics for IBM Lotus Notes developers - Some additions?

Posted in @Formula, Applications, LotusScript, lotus Domino, lotus notes by quintessens on May 17th, 2008

Andre Guirard wrote a nice interesting piece of document regarding considerations for application development. The document you can download here.

Recently I have asked to write down some ‘best practices’ from a developer perspective. I see a lot of Andre’s writing return in this, also since most of my work was ‘just’ a collection of documents / papers / hints & tips I have collected the past years.

I will wrote some additional performance considerations which I did not find back in Andre’s work:

Use @trim in all translation events for fields type text, this avoids that you later have to use it in view columns

@Trim(@ReplaceSubstring(@ThisValue; @Char(9):@Newline; ” “))

—-

Make sure when using subforms something is written in Globals of the subform, it dus not matter what (Notes only)

Dim loadfaster As Integer
loadfaster =True

The more subforms you have the more effect code in Globals has.

—-

Don’t use “Print” so much (LotusScript)

Minimize the use of the Print statement when performing actions in long loops. Printing a lot of messages to the status bar or console uses a surprising amount of memory, and it can have a noticible impact on an otherwise fast-running loop. Specifically, the memory used by each Print statement is not released until the entire agent is finished.

—-

Avoid large or complex tables

Large or complex tables can greatly affect the speed with which a form can be rendered (both in the client and in the browser). In particular, avoid tables with a huge number of rows, a lot of nesting, or a large number of hide-when conditions.

—-

For agents: avoid Reader fields when possible

Using Reader fields can slow down all of your document processing code across the board, because the database has to evaluate the reader fields for each document before it can even attempt to do anything with them. This is especially true when the agent signer doesn’t have access to a large number of documents in the database, because the documents that aren’t visible still have to be evaluated before they can be skipped.

—-

Share NotesView References

If you need to use a reference to a view multiple times in your code, get the view only once and share the reference (either using a global or static variable, or by passing a NotesView object as a parameter in functions/subs/methods). Accessing views using getView is a very expensive operation.

—-

Use @Function Agents For Simple Document Modifications

If you just need to modify, add, or delete one or several [non rich-text] fields in all or selected documents in a view, it is much faster to use an agent with simple @Functions (rather than LotusScript or Java) that runs against all or selected documents. 

—-

Resize Arrays Infrequently

ReDim your arrays as infrequently as possible. However, if you have to append data to an array quite often, Redim Preserve is MUCH more efficient than many calls to ArrayAppend

Default view

Whenever your application is opened, your default view is opened. Always be sure that this is a view that opens quickly.

—-

Use buttons and picklists instead of drop-down lists

In some cases, there are so many drop-down lists, and they’re so large and used so frequently, that the only reasonable solution is to stop using them on the main form. The line of reasoning to use is to ask yourself the following questions:

  • How many times will a document be read in its life?
  • How many times will a document be edited?
  • Of the times it’s edited, how many times will these lookups be required?

—-

Use DigestSearch Method

For searching server-based databases from a Notes client, DigestSearch is twice as fast as any other search method available, outperforming both full-text search and LotusScript’s GetDocumentByKey method. You call DigestSearch using this LotusScript command:

Set doc=FastSearchByKey(db, “searchword”)

Like I said before, this is ‘collected work’ so I take no responsibility if it would not increase performance. See it as food for thought =)

Mimicing the tabbed table function

Posted in @Formula, Show N Tell Thursday, development, lotus notes, sntt by quintessens on March 20th, 2008

tab1The problem with the Notes tabbed table is that you can not, by default, add an action when you click on a tab.

I created a tabbed table function in the way you probably would do it on the web (with hidding/showing divs) so a tab is just a table column with a text hotspot in it. When you click on the text a different table would be shown with an ‘active tab’ highlighted:

dialogbox

The reason for this solution is that the form is presented in a dialogbox and I have to set a parameter from which tab the user has selected a document-template (templates may be stored in different databases).

So how to fix this with a Notes tabbed table outlook?

First create images for all active tabs:

tab1 & tab2

Then sharpen your eyes (or use good photo-editor program) and select a part of the horizontal row/border that aligns the table to the width of the page (correctly described?):

This is an enlarged example:

zoomed

Now place your tabbed images in separate tables, give the table a background color and a cell image of the ‘zoomed’ example:

 table properties

On your ‘tabbed’ images you place hotspot rectangles that will do the hide/when-work. Here you can write now also the action that is not possible for a normal ‘tabbed table’:

hotspot formula

At the end the result will look something like this:

result tabbed mimic

Language support in DB

Posted in @Formula, JSON, JavaScript, Prototype, Sandbox, lotus Domino by quintessens on November 2nd, 2007

While waiting on the next plane I write this little posting about a simple technique used in the very nice !!Help!! application available on OpenNTF which give a basic support for different languages support across an application.

Actually I was looking at a more advanced solution using Yahoos Translator utily, but time made me decide to the most easy/quickest approach making the most benefit of Domino functions itself.

here is how it looks like

While my main focus is development for the web the technique can still be easily used across forms. Here is how it works:

  • In Notes documents you define the values for each language you want to suppport, mostly these values are grouped by the design element you use them in or the specific type of element they present (field labels, action buttons… things like that)

language notes documents

  • On the design element itself you make a connection to the preferred language selected by the user, in my example it is a normal document that is being treated like a ‘profile document’:

language support on form

  • Add lookup fields for each ‘language’ document you have created:

language field lookups

  • On the place in the design element where you want to show/use the related value you strip the results and return the result:

showing the value(s)

That’s it! But… wait. This does not work for buttons on a form because Domino allows not a computed value/label for it.

JSON to the rescue!

The most easiest way to get the values in an array I thought was transforming them in a JSON format wia basic @Functions:

@functions presing JSON array

With this array I easily approach all my <input> type buttons via Prototype and replace their values with the value defined in the array:

transforming the buttons

A downloadable working example can be found here. I wonder which language support systems you are using?

Scriptaculous autocompleter in Domino form

Posted in @Formula, AJAX, JSON, JavaScript, Prototype, Sandbox by quintessens on November 1st, 2007

This post describes briefly an implementation of Scriptaculous’ Ajax.Autocompleter class.

It uses an AJAX request using a Page element for an @DBLookup I believe first mentioned at Lotusphere 2007 by Jack Rattcliff and later (?) by Jake Howlett in his http://www.codestore.net/store.nsf/unid/BLOG-20060221 and perfectionized (?) by my collegue Tomas.

Scriptaculous autocompleter needs an unordered list in return. For instance this list might be returned after the user typed the letter “y”:

<ul>
    <li>your mom</li>
    <li>yodel</li>
</ul>

Some examples use an agent to return the unordered list, others describe using a page.

The demo allows you to fill in multiple fields after clicking on one of the presented suggestions by splitting the responseText:

autocompleter form

In demo-mode it would look something like this:

demo autocompletion

For downloading a working example click here.

@If variant for aliases

Posted in @Formula, Show N Tell Thursday, lotus notes, sntt by quintessens on October 25th, 2007

For a computed text I needed to write down the value of a dialoglist field which uses aliases.

Luckily the number of options was limited, but for a list of long options I do not want to use this construction:

@If( condition1 ; action1 ; condition2 ; action2 ; … ; condition99 ; action99 ; else_action )

Therefor I came up with the following solution creating a text-list with options and then go through them using @Elements, @Right and @Left:

status:=”Draft|0″:
“Awaiting Approval|1″:
“Approved|2″:
“Archived|3″:
“Rejected|91″:
“Returned|92″;

@For(n := 1;n <= @Elements(status); n := n+1;
 rightvalue:=@Right(status[n];”|”);
 @If(rightvalue=Tx_Status;leftvalue:=@left(status[n];”|”);”")
);
leftvalue;

Export to Excel wizard

Posted in @Formula, Applications, LotusScript, Show N Tell Thursday, lotus notes, sntt by quintessens on September 27th, 2007

I was asked to take a look at an existing application, and come up with suggestions to improve performance.

In a first look I found that most of the views, especially the default to be opened view, were set to refresh the index automatically… you can imagine how much the users love to start working with this application which has readers fields on all documents (NOT!).

When I asked were all the views were needed for, I got as reply: to have it in a format we want to make graphics in Excel.

Okej, why not delete these ‘views to be exported for Excel’ and replace them with an export functionality?

export start button

So what where the needs?

  1. a set of fields for different types of forms that should be manageble by the application manager
  2. an export-mechanism to Excel which should be easy to understand for end-users and give them the opportunity to create customizable exports.

1) Export profiles

In order to create so-called Export profiles for different Notes forms I needed an additional form (ExcelFormExport) where the administrator can:

  • select the desired form for export (FieldA)
  • select the form-fields that should be available for export (FieldB)

This last field is being calculated when FieldA is being exited:

Sub Exiting(Source As Field)
 Dim workspace As New NotesUIWorkspace
 Dim uidoc As NotesUIDocument
 Set uidoc = workspace.CurrentDocument 
 fieldvalue = Lcase(uidoc.FieldGetText(”Tx_FormSelected”))
 Dim doc As NotesDocument
 Set doc = uidoc.document
 If fieldvalue <> “” Then
  Dim session As New NotesSession
  Dim db As NotesDatabase
  Set db = session.CurrentDatabase  
  Forall form In db.Forms
   If Lcase(form.Name) = fieldvalue Then
    If Isempty(form.Fields) Then
     Messagebox form.Name & ” has no fields…”
    Else
     ’collecting the form fields
     Dim arrFieldNames() As String
     Dim iCount As Integer
     iCount = 0     
     Redim arrFieldNames(0)
     Forall field In form.Fields      
      Redim Preserve arrFieldNames(iCount)
      arrFieldNames(iCount) = field
      iCount = iCount + 1
     End Forall     
     Call doc.ReplaceItemValue(”Tx_FormFields”, BubbleSort(arrFieldNames))     
     Call uidoc.Refresh()     
    End If
    Exit Sub
   End If
  End Forall
  Messagebox “The form “”" & formNameIn & “”" does not exist”
 End If
End Sub

Bubblesort is a function which sorts the array with fieldnames:

Function BubbleSort(vtList As Variant) As Variant
 Dim tmpValue As Variant
 Dim x As Integer, y As Integer 
 If Ubound(vtList) = 1 Then
  BubbleSort = vtList
  Exit Function
 End If 
 For x = 0 To Ubound(vtList)
  For y = (x + 1) To Ubound(vtList)
   On Error Resume Next
   If vtList(x) > vtList(y) Then
    tmpValue = vtList(x)
    vtList(x) = vtList(y)
    vtList(y) = tmpValue
   End If
  Next
 Next 
 BubbleSort = vtList 
End Function

Luckily I could make use of a solution from my colleague Tomas Ekström (his so-called ‘tablewalker’) which made the selection of fields and giving them for the end-user understandable ‘labels’ in the wizard:

export profile form

The next step was creating a dialog between the available ‘Export profiles’ and the Export to Excel solution written by Ken Pespisa.

 dialog for selection source

In order to display the ‘Export profiles’ in the dialoglist I had to re-write Ken’s solution a bit. I advice you to use Ken’s standard solution in stead.

Well, the rest of this export wizard is just following Ken’s work.

 select fields for export dialog

What I tried to achieve with this solution is:

  • having an easy way for an administrator to define ‘Export profiles’ based upon information on Notes forms.
  • presenting an understandable interface for the end-user that enables to create customizable exports to Excel
  • get rid of a large set of nasty Notes views!

A demo you can download here.

Newsletter profile

Posted in @Formula, LotusScript, Show N Tell Thursday, lotus notes, sntt by quintessens on September 6th, 2007

Currently I am working on a function where users can create a profile for a newsletter. In the profile the user can specify a search query and some options.

It all begins with a simple View Action button:

profile form

The button will check if for the current user already a profile document exists, if so the document will be opened in edit mode, otherwise the form will be opened (the client requested to make all profiles public…)

varUser:=@Name([Abbreviate];@UserName);

@If(
 @IsError(@DbLookup(”":”NoCache”;”";”v-userprofiles”;varUser;”Tx_DocID”));
  @Do(
   @Command([Compose];”F-PU”)
  );
  @Do(
   @Command([OpenView];”v-userprofiles”;varUser);
   @Command([OpenDocument];”1″)
  )
 )

The form itself is pretty plain and simple. The user can add more criteria to the search query via ‘AND’ or ‘OR’ options. Default we search on only one type of document.

While specifying the criteria, the user sees what he/she is adding to the query:

the profile form

The code behind the ‘ADD’ button is this:

varForm:= “Document”;
varField:=Tx_Fields;
varValues:=@Implode(”\”" + Tx_Values + “\”" ;”:”);

@If(Tx_Option=”AND”;
 @Do(
  varConstructor:= ” & ” +  varField + ” = ” + varValues;
  @SetField( “Tx_Query” ; Tx_Query + varConstructor );
  @SetField( “Tx_LastValue” ; varConstructor )
 );

 @Do(

 @If(@Left(@GetField( “Tx_LastValue” );”)”)=”";
  @Do(
   varPrevValue:= @Right(@GetField( “Tx_LastValue” );”& “);
   varConstructor:=  ” & ( ” + varPrevValue + ” | ” +  varField + ” = ” + varValues + ” )”
  );
 @Do(
  varPrevValue:= @LeftBack(@GetField( “Tx_LastValue” );”)”);
  varConstructor:=  varPrevValue + ” | ” +  varField + ” = ” + varValues + ” )”
 ));
 varNewQuery:= @ReplaceSubstring( Tx_Query; Tx_LastValue ; varConstructor );
 @SetField( “Tx_Query” ; varNewQuery );
 @SetField( “Tx_LastValue” ; varConstructor )
));

@SetField( “Tx_Fields” ; “”);
@SetField( “Tx_Values” ; “”);
@SetField( “Tx_Options” ; “”)

I also added a button so the user can check if the specified query results in any usefull document collection. The code behind this button is:

Sub Click(Source As Button) 
 Dim workspace As New NotesUIWorkspace
 Dim session As New NotesSession
 
 Dim db As NotesDatabase
 Set db = session.CurrentDatabase
 
 Dim uidoc As NotesUIDocument
 Set uidoc = workspace.CurrentDocument 
 
 searchFormula$ = uidoc.FieldGetText( “Tx_Query” )
 Dim collection As NotesDocumentCollection 
 Set collection = db.Search(searchFormula$, Nothing, 0)
 numDocs% = collection.count
 If numDocs% = 0 Then
  Messagebox “There are no matching documents” , , “Search Result”
  Exit Sub
 Else
  answer% = Messagebox ( (numDocs% & ” document(s) match this query. “), MB_OKCANCEL + MB_ICONQUESTION, “Search Result”)
 End If
End Sub

Because the Newsletter function has to run on a scheduled basis I added some additional options:

  • How often a Newsletter should be sent
  • The number of document links in the Notes Newsletter
  • How old corresponding documents may be

Note: in the timestamp you can add for a search Notes uses the modified date, so this is the date the document has been modified (nice if you working on a test environment with a copy so all the documents ARE modified). You can also add this option as criteria in your search formula ofcourse.

On the bottom of the form I added a button where the user can instantly receive a Notes Newsletter with everything he/she has specified. The code for the button is:

Sub Click(Source As Button) 
 Dim workspace As New NotesUIWorkspace
 Dim session As New NotesSession
 
 Dim db As NotesDatabase
 Set db = session.CurrentDatabase
 
 Dim uidoc As NotesUIDocument
 Set uidoc = workspace.CurrentDocument  
 Dim doc As NotesDocument
 Set doc = uidoc.Document
 
 searchFormula$ = uidoc.FieldGetText( “Tx_Query” ) 
 maxListSize = uidoc.FieldGetText( “Tx_QueryMax” )
 daysAdjust = doc.Tx_QueryDays(0) 
 Dim dateTime As New NotesDateTime( Now )
 Call dateTime.AdjustDay( -daysAdjust )
 
 Dim collection As NotesDocumentCollection
 Set collection = db.Search(searchFormula$, dateTime, Cint(maxListSize))
 numDocs% = collection.count
 If numDocs% = 0 Then
  Messagebox “There are no matching documents” , , “Search Result”
  Exit Sub
 Else
  answer% = Messagebox ( (numDocs% & ” document(s) found. Creating a Newsletter…”), MB_OKCANCEL + MB_ICONQUESTION, “Search Result”)
  
  Dim newsletter As NotesNewsletter
  Set newsletter = New NotesNewsletter( collection )  
  newsletter.DoSubject = True
  newsletter.SubjectItemName = “Tx_Title”  
  Dim newsletterdoc As NotesDocument
  Set newsletterdoc = newsletter.FormatMsgWithDoclinks( db )  
  newsletterdoc.Form = “Memo”
  newsletterdoc.Subject = “Business Intelligence Newsletter - Your results”
  Call newsletterdoc.Send( False, uidoc.FieldGetText( “Tx_UserName” ))  
  
 End If
End Sub

Some parts of the code above I will re-use in the scheduled agent that sends out the Newsletters on a scheduled base.

Wrap-up: a simple handy function that might save you creating some additional views…

(User) Profile documents & mandatory fields

Posted in @Formula, JavaScript, LotusScript, Show N Tell Thursday, lotus Domino, sntt by quintessens on August 30th, 2007

It has been a while since I wrote here something so I will just write something I did today. Right now I am doing some prototyping for a demo-application (web) which generally will be an ordering system.

The application uses currently (user) profile documents. It was a bit hard to find code to copy & paste, because most examples where not so detailed (just words, no code) or they used actual Notes documents, not profile documents.

So how does it work? When opening the form via an url (form_name?OpenForm) a WebQueryOpen Agent is initiated which runs the following simplified code:

Sub Initialize
 Dim s As New NotesSession
 Dim db As NotesDatabase
 Dim profiledoc As NotesDocument
 Set db = s.CurrentDatabase
 Set profiledoc = db.GetProfileDocument(”UserProfile”, s.UserName) 
 Dim currentdoc As NotesDocument
 Set currentdoc = s.documentcontext 
 currentdoc.Tx_UserLanguage = profiledoc.Tx_UserLanguage(0)
 currentdoc.Tx_UserCostCenter = profiledoc.Tx_UserCostCenter(0)
 currentdoc.Tx_UserGLAccount = profiledoc.Tx_UserGLAccount(0) 
 currentdoc.Tx_UserTechName = profiledoc.Tx_UserTechName(0)
 currentdoc.Tx_UserTechPhone = profiledoc.Tx_UserTechPhone(0)
 currentdoc.Tx_UserDeliveryStation = profiledoc.Tx_UserDeliveryStation(0) 
End Sub

So basically it picks (when available) the profiledoc and copies values from it to the opened form. I could have used IsProfile to check if we actually working with a profile document…

In the WebQuerySave event I run a different code which writes away information entered in the form to the profile document:

Sub Initialize
 Dim s As New NotesSession
 Dim db As NotesDatabase
 Dim profiledoc As NotesDocument
 Set db = s.CurrentDatabase
 Set profiledoc = db.GetProfileDocument(”UserProfile”, s.UserName) 
 Dim currentdoc As NotesDocument
 Set currentdoc = s.documentcontext 
 profiledoc.Tx_UserLanguage = currentdoc.Tx_UserLanguage(0)
 profiledoc.Tx_UserCostCenter = currentdoc.Tx_UserCostCenter(0)
 profiledoc.Tx_UserGLAccount = currentdoc.Tx_UserGLAccount(0) 
 profiledoc.Tx_UserTechName = currentdoc.Tx_UserTechName(0)
 profiledoc.Tx_UserTechPhone = currentdoc.Tx_UserTechPhone(0)
 profiledoc.Tx_UserDeliveryStation = currentdoc.Tx_UserDeliveryStation(0) 
 Call profiledoc.Save( True, False )
End Sub

Later I think will use a similar functionality to write information back to the profile document, that hasn’t been entered before in the user profile, from any sensible point in the application (like in a final order form)…

The first ‘problem’ i faced was that a ‘normal’ JavaScript submit would give me a ‘form processed’ message.  Since I had a nice ‘update’ button in mind so the user should stay in the form opened I was ‘forced’ to use a common known Domino ‘work around’.

updatebuttons

The reference under the ‘Save Setting’ link inititates a JS function which actually initiates the function under a Notes button, that is being stored in a hidden <div>

function submitForm(){
 document.all.MySaveButton.onclick();
}

hidebutton

So the JS function initiates the @Command([FileSave]) function which initiates the WebQuerySave event and thereby the user profile is being updated on the background.

Since the document will be re-opened due to a refresh (don’t ask me why what forces this) the WebQueryOpen events initiates again and the saved information is being re-placed on the refreshed form…

So far so good, where is the news?

I wanted to make visible which fields are supposed to be mandatory and highlight them like in the next example:

mandatory empty

Here you have ‘normal’ fields with a gray border, and ‘normal’ entry fields that are supposed to be also ‘mandatory’. So for these fields I add both classnames.

field properties

On the JS onload event I initiate the following function:

checkISValid();

The code for this function is pretty basic and contains (in this example) no actualy validation, just a check if the field is empty or not:

function checkISValid(){
 var checkFields = new Array();
 checkFields = getElementsByClassName(document, “input”, “mandatoryField”)
 for (x in checkFields){  
  if (checkFields[x].value != “” ){
   checkFields[x].className = “entryField”; 
  }
 }
}

So if the field with classname mandatoryField is empty it will get the mandatoryField class ‘removed’ (overwritten) and it will look like this:

mandatory field entered

I think in this way it is pretty clear for the user which information is still missing…

Here is the ‘getElementsByClassName’ function borrowed from this site:

function getElementsByClassName(node,tag,searchClass) {
 var classElements = new Array();
 if ( node == null )
  node = document;
 if ( tag == null )
  tag = ‘*’;
 var els = node.getElementsByTagName(tag);
 var elsLen = els.length;
 var pattern = new RegExp(”(^|\\s)”+searchClass+”(\\s|$)”);
 for (i = 0, j = 0; i < elsLen; i++) {
  if ( pattern.test(els[i].className) ) {
   classElements[j] = els[i];
   j++;
  }
 }
 return classElements;
}

Ps. for some nice JS onFocus effect like this:

on focus effect

You could add something like this:

on focus JS code

Don’t forget to reset coloring on the onBlur event ;-)

Creating a ‘context-sensitive’ help (Notes client)

Posted in @Formula, LotusScript, Show N Tell Thursday, lotus notes, sntt by quintessens on May 17th, 2007

Mostly providing a help function in a database is a step that comes long after an application has been put ‘live’.

In order to save myself a lot of design additions I created a context-sensitive ‘help’ function for application managers so they can add them later, when users come with the need for documentation.

The function I add on view- and formlevel wherever I think it might be handy in the future and is really easy to implement.

On a view or a form I add an action button with some @formula code.

On a view:

db:=@Subset(@DbName; -1);
@If(@IsError(@DbLookup(”":”NoCache”;”"; “(Help)”; @ViewTitle+”\\”+db;1));
@If(@IsMember(”[Admin]“;@UserRoles);
@Do(
 @Set(”tmpval”;@Prompt([YesNo];”Help module”;”There is no help for this view available. Would you like to create a new Help document?”));
 @If(tmpval;@Command([ToolsRunMacro];”(CreateHelpView)”);”"));
@Prompt([Ok];”Help module”;”There is no help for this view available.”));
@Command([OpenHelpDocument]; “”; “(Help)”; @ViewTitle+”\\”+db))

On a form:

db:=@Subset(@DbName; -1);
@If(@IsError(@DbLookup(”":”NoCache”;”"; “(Help)”; Form +”\\”+db;1));
@If(@IsMember(”[Admin]“;@UserRoles);
@Do(
 @Set(”tmpval”;@Prompt([YesNo];”Help module”;”There is no help for this form available. Would you like to create a new Help document?”));
 @If(tmpval;@Command([ToolsRunMacro];”(CreateHelpDoc)”);”"));
@Prompt([Ok];”Help module”;”There is no help for this form available.”));
@Command([OpenHelpDocument]; “”; “(Help)”; Form+”\\”+db))

If a user has the role Admin assigned it will enable to create a new ‘Help’ document for this particular design element.

If there is no ‘Help’ document available for this design element a popup window will appear:

help popup

When selecting ‘No’ the code will stop. When selecting ‘Yes’ an agent will run and creates the document on the background. A confirmation will appear:

help confirm

If the user hits the action button again the newly created document will appear in a new window, just like the normal client Help function would do. This enables the user to switch between the application and the ‘Help’ document.

If you add the Admin role in an Authors field on the ‘Help’ document they can edit the document via a double-click and add content to the document.

At least this function saved me a lot of time and surprised application managers that it was already there from scratch!

Here is the code for the agent:

Sub Initialize
%REM
———————————————————————————————————————
The next script runs when there is no help document available for this design element
The script will create a new help document
———————————————————————————————————————
%END REM
 Dim Session As New NotesSession
 Dim workspace As New NotesUIWorkspace
 Dim db As NotesDatabase
 Set db = session.CurrentDatabase
 Dim lookupview As NotesView 
 Dim uidoc As NotesUidocument
 Dim thisdoc As notesdocument
 Dim lookupdoc As notesdocument
 Dim kolldoc As notesdocument
 Dim kollview As notesview
 Dim newhelpdoc As notesdocument
 Set uidoc = workspace.CurrentDocument
 Set thisdoc = uidoc.Document
 Dim helpform As String
 Dim server As String
 Dim path As String
 
 path = db.FilePath
 helpform=thisdoc.Form(0)
 
 ’Collect data from help view
 Set kollview = db.GetView( “(Help)” )
 key$ = helpform+” “+path
 Set kolldoc = kollview.getdocumentbykey(key$, True)
 If  kolldoc Is Nothing Then
  
  ’Create a new help document
  Set newhelpdoc = db.CreateDocument
  newhelpdoc.form=”F-Help”
  newhelpdoc.Tx_About=”Instructions for: ” + helpform
  newhelpdoc.Tx_Databas=path
  newhelpdoc.Tx_Form=helpform
  saveres = newhelpdoc.save(True,True)
  If saveres Then
   var = Messagebox(”A help document has been created in the Help module.”+Chr(10)+Chr(10)+”Please test the connection via the Help button.”,0+64,”Help module”)
  Else
   var = Messagebox(”A Help document could not be created. Please check the connection to the Help module.”,0+16,”Help module”)
  End If
 Else 
  var = Messagebox(”A Help document already exists for this form.”,0+16,”Help module”)
 End If    
 
End Sub

A sortable Bob Obringer view

Posted in @Formula, JavaScript, Show N Tell Thursday, sntt by quintessens on April 26th, 2007

You know what they say about customers: If you give them one finger, they’ll take your hand.

After implementing Bob Obringer’s approach to add page navigation to a view I get the request to make the columns sortable.

Normally I offer customers sortable columns as clickable headers in a table, like described here. But this approach makes the documents presented in the table part of the sorting. So if your table displays 30 documents based upon a server setting, only these 30 presented documents are part of the sorting process.

If you implement this approach in Bob’s solution which is based on showing only a subset of documents at a time the function will also only sort the documents presented at that time.

When looking at the parameter Notes adds when clicking a sortable Notes viewcolumn it will be

  • &ResortAscending=
  • &ResortDescending=

followed by the number of the column you clicked on.

Don’t ask why but this parameter seems to be displayed always before the number of documents shown, if you manually change the order of parameters it will present the same result. So:

  • &Count=30&ResortDescending=3 equeals
  • &ResortDescending=3&Count=30

So in order to make a column in Bob’s approach sortable you need to rewrite the follwoing sections in the JS part:

function buildLink(pageNum, text) {
…. 
linkHTML = linkHTML + “onclick=document.location.href=’” + viewAlias + “?OpenView&Start=” + startLinkDoc + “&Count=” + docsPerPage + “‘>” + text + “</td>”;

}

into:

function buildLink(pageNum, text) {

linkHTML = linkHTML + “onclick=document.location.href=’” + viewAlias + “?OpenView” + sortQuery + “&Start=” + startLinkDoc + “&Count=” + docsPerPage + “‘>” + text + “</td>”;

}

and

function getPage(event, field) {
…  
document.location.href = dbPath + viewAlias + “?OpenView&start=” + (((newPage - 1) * docsPerPage) + 1) + “&count=” + docsPerPage

}

into:

function getPage(event, field) {

document.location.href = dbPath + viewAlias + “?OpenView” + sortQuery + “&start=” + (((newPage - 1) * docsPerPage) + 1) + “&count=” + docsPerPage

}

So what is in the sortQuery variable?

In the HTMLHead Content section I define:

varColumnAsc:=@Left(@Right(Query_String_Decoded;”&ResortAscending=”);2);
varColumnAsc:=@If(@Right(varColumnAsc;1)=”&”;@Left(varColumnAsc;1);varColumnAsc);

varColumnDesc:=@Left(@Right(Query_String_Decoded;”&ResortDescending=”);2);
varColumnDesc:=@If(@Right(varColumnDesc;1)=”&”;@Left(varColumnDesc;1);varColumnDesc);

sortQuery:=
@If(@Contains(Query_String_Decoded;”Ascending”);”&ResortAscending=” + varColumnAsc;
 @If(@Contains(Query_String_Decoded;”Descending”);”&ResortDescending=” + varColumnDesc;”"));

I also define in the HTMLHead Content:

“<script language=’javascript’>” + @NewLine +
 ”var sortQuery = ‘” + sortQuery + “‘” + @NewLine +
“</script>”

The code assumes you have less than 100 columns in the view. If you have less than 10 columns for example 9:

@Left(@Right(Query_String_Decoded;”&ResortAscending=”);2);

results in ‘9&’, ’&’ is the beginning of the next parameter ‘&Count=’.

Therefor I correct this with:

@If(@Right(varColumnAsc;1)=”&”;@Left(varColumnAsc;1);varColumnAsc);

so the ‘&’ gets cut.

sortable column