LS2J rules!

I know it has limitations and that it has issues but today it worked like a charm. I was asked to write a library in LotusScript to resolve groups and nested groups from Active Directory into an array of users. It had to be cross platform which ruled out COM and ActiveX controls even if I had been able to find any.

The solution was of cause to write the code in Java using JNDI (Java Naming and Directory Interface) and then write a thin wrapper class in LotusScript using LS2J (LotusScript to Java bridge supplied with Notes out-of-the-box). Now I have a cross-platform solution that has no external dependencies. Beautiful!

So learn Java already! 🙂

Discovering Notes 8: Automatic loading of images in HTML newsletters


In Notes 8 there is this nice new and improved display of HTML newsletters. An additional nice feature is that images are prevented from loading to combat SPAM bots that harvest e-mail addresses by using dynamic image URLs. See below for a screenshot of how a HTML newsletter looks before images are allowed.

It would however be nice if I could setup a list of e-mail addresses where image loading was enabled by default such as for my favorite HTML newsletters. Well it isn’t available out of the box with Notes 8 but you could easily write it yourself as an agent that processes incoming e-mail.

The agent only needs to set a text field called $DelayedImagesOK to the value “ok” (no quotes) for the e-mails where image loading should be enabled upfront.

Happy Friday!

Lotus Notes and Domino 8: What’s New in LotusScript and Formula Language

Rocky posted his slides from his “Lotus Notes and Domino 8:
What’s New in LotusScript and Formula Language”-session from the Advisor summit. Besides being an interesting read it had two slides that really caught my attention (slide 11 and slide 25).

When describing what’s new in the upcoming Notes 8.0.1 (1Q2008) he mentioned a welcome change to a dear old friend: @Now (see slide 11):

  • @Now([NoCache]) – when checking the time
    on a server forces a server transaction to fetch
    the time.
  • Without this keyword, @Now would cache the
    server time on the first call, and compute the
    current time by adding the cached time and
    elapsed time.
  • This causes problems if the system time on the
    client or server machine changes once the server
    time is cached.
  • Note that using [NoCache] adds the significant
    overhead of doing a server transaction on each
    call.

On slide 25 Rocky mentions two additional commands to the ReadViewEntries beside getting the output in JSON instead of DXL. The two new nice parameters are &EndView and &KeyType=<text/time/number>. &EndView returns entries from the end of the view and &KeyType allows you to specify the datatype of the key which allows you to use other key types than text.

Discovering Notes 8: Web service enabled script libraries


In a recent edition of the LotusUsergroup.org newsletter Julian listed some of the new AppDev features of Notes 8. Among them were the new web service enabled script libraries.

Basically what you do is simply pointing a script library to a WSDL file and some stub code is automatically generated for you. Then when you use the script library from agents etc. the methods you call will automatically be forwarded to the actual web service without you worrying about SOAP, HTTP etc. It actually is that easy!

To test it out I found an example of a public stock quote web service (http://www.webservicex.net/stockquote.asmx?WSDL) and used that for an example.

Start by creating a new empty LotusScript script library. Now click the “WSDL…” button at the bottom of Domino Designer and simply select “Import WSDL”. Now paste the above URL into the filename field and click Open (it’s really nice that you can actually simply paste the WSDL URL into the dialog instead of first saving the WSDL to a local file). Now your script library should look like this:

%INCLUDE "lsxsd.lss"
Const n4 = "http://www.webserviceX.NET/"
Class StockQuoteSoap_n4 As PortTypeBase

  Sub NEW
    Call Service.Initialize ("HttpWwwWebserviceXNETStockQuote", _
    "StockQuote.StockQuoteSoap", _
    "http://www.webservicex.net/stockquote.asmx", _
    "StockQuoteSoap_n4")

  End Sub

  Function GetQuote(symbol As XSD_STRING) As XSD_STRING
    Set GetQuote = Service.Invoke("GetQuote", symbol)
  End Function

End Class

Now save the script library and call it “Stock quote”.

Create a LotusScript agent and in Options import the script library (Use “Stock quote”). Now we want to call the script library which is done using code like this:

Sub Initialize
  Dim ws As New NotesUIWorkspace
  Dim sq As New StockQuoteSoap_n4()
  Dim symbol As String
  Dim symbols(2) As String
  symbols(0) = "IBM"
  symbols(1) = "JAVA"
  symbols(2) = "AAPL"
  symbol = ws.Prompt(4, "Stock symbol", _
    "Select stock for quote...", "", symbols)
  If symbol = "" Then
    Exit Sub
  Else
    Print "Using symbol: " + symbol
  End If

  Dim sh As New XSD_STRING()
  Call sh.setValueFromString(symbol)
  Set sh = sq.GetQuote(sh)
  Msgbox sh.getValueAsString()
End Sub

Now when you run the agent and you select a stock symbol it will actually automatically go out to the web service, get the quote and return it to the caller. All this with the code I just showed you. Nice!

Stand by for a post on the nitty, gritty details…

LocationDocument LotusScript class updated

I updated my LocationDocument LotusScript class again for Location type (Local Area Network, No connection etc.) and for mail server name. It makes it really easy to quickly get at location document information from script.

See the original post for usage.

'/**
' * LotusScript class for interacting with location documents
' * in the personal name and address book.
' */
Public Class LocationDocument
   'declarations
   Private pSession As NotesSession
   Private pDb As NotesDatabase
   Private pDoc As NotesDocument

   '/**
   ' * Constructor
   ' */
   Public Sub New(get_location As String)
      'declarations
      Dim viewNab As NotesView
      Dim nn As NotesName
      Dim ini_location As String
      Dim location As String
      Dim comma As Integer

      'get session
      Set Me.pSession = New NotesSession()

      If get_location = "" Then
         'get the location from notes.ini
         ini_location = Me.pSession.GetEnvironmentString("Location", True)

         'parse out the location name if none specifed in the constructor
         comma = Instr(1, ini_location, ",")
         location = Mid$(ini_location, 1, comma-1)
      Else
         'use supplied location name
         location = get_location
      End If

      'abbreviate the name
      Set nn = New NotesName(location)
      location = nn.Abbreviated

      'get database and view
      Set Me.pDb = Me.pSession.GetDatabase("", "names.nsf")
      Set viewNab = Me.pDb.GetView("Locations")

      'lookup location document
      Set Me.pDoc = viewNab.GetDocumentByKey(location, True)
      If Me.pDoc Is Nothing Then
         Error 9999, "Location document not found in names.nsf"
      End If
   End Sub

   '/**
   ' * Saves.
   ' */
   Public Sub Save()
      Call Me.pDoc.Save(False, False)
   End Sub

   '/**
   ' * Property to get the backend document.
   ' */
   Public Property Get Document As NotesDocument
      Set Document = Me.pDoc
   End Property

   '/**
   ' * Property to get the e-mail address.
   ' */
   Public Property Get EmailAddress As String
      EmailAddress = Me.pDoc.GetItemValue("ImailAddress")(0)
   End Property

   '/**
   ' * Returns the name of the directory server.
   ' */
   Public Property Get DirectoryServer As NotesName
      If Me.LocationType = "3" Then
         Set DirectoryServer = Nothing
      Else
         Dim nn As New NotesName(Me.pDoc.GetItemValue("DirectoryServer")(0))
         Set DirectoryServer = nn
      End If
   End Property

   '/**
   ' * Sets the name of the directory server.
   ' */
   Public Property Set DirectoryServer As NotesName
      Call Me.pDoc.ReplaceItemValue("DirectoryServer", DirectoryServer.Canonical)
   End Property

   '/**
   ' * Returns the name of the Sametime server.
   ' */
   Public Property Get SametimeServer As NotesName
      If Me.LocationType = "3" Then
         Set SametimeServer = Nothing
      Else
         Set SametimeServer = New NotesName(Me.pDoc.GetItemValue("SametimeServer")(0))
      End If
   End Property

   '/**
   ' * Sets the name of the Sametime server.
   ' */
   Public Property Set SametimeServer As NotesName
      Call Me.pDoc.ReplaceItemValue("SametimeServer", SametimeServer.Canonical)
   End Property

   '/**
   ' * Returns the name of the mail server.
   ' */
   Public Property Get MailServer As NotesName
      If Me.LocationType = "3" Then
         Set MailServer = Nothing
      Else
         Set MailServer = New NotesName(Me.pDoc.GetItemValue("MailServer")(0))
      End If
   End Property

   '/**
   ' * Sets the name of the mail server.
   ' */
   Public Property Set MailServer As NotesName
      Call Me.pDoc.ReplaceItemValue("MailServer", MailServer.Canonical)
   End Property

   '/**
   ' * Returns the location type.
   ' * 
' * Local Area Network = "0" ' * Notes Direct Dialup = "1" ' * Network Dialup = "4" ' * Custom = "2" ' * No connection = "3" ' */ Public Property Get LocationType As String LocationType = Me.pDoc.GetItemValue("LocationType")(0) End Property End Class

RFC821 address functions in Notes – are they working properly?

I have been trying to use the built-in RFC821 address functions in Notes recently but has given up since they do not work correctly in my mind. A RFC821 – or an internet address – is in my mind composed of a local part (the part before the @-sign) and a period separated domain component (the part after the @-sign). A valid RFC821 address must contain both parts. Valid examples could be:

  • jdoe@example.com
  • john.doe@example.com
  • jdoe@sales.example.com

Notes/Domino has functions in @Formula, LotusScript and Java for working with and validating RFC821 addresses and none work as expected (probably because they all use the same C code). For example all functions will accept “jdoe@example” as a valid RFC821 address – an address which is my mind isn’t valid at all. This fact is further aggravated by the fact that I would expect to use the functions to distinguish between Notes addresses and RFC821 addresses. When the functions “work” as they do I cannot do that since jdoe@example could just as well be a Notes shortname followed by a Domino domain.

Below is a test case for LotusScript (non-blank value should indicate a valid RFC821 address).

Dim nn As New NotesName("jdoe@example.com")
Print nn.Addr821
>> jdoe@example.com
Set nn = New NotesName("jdoe@example")
Print nn.Addr821
>> jdoe@example

The help states:

The RFC 821 address is a name followed by an at sign followed by an organization, for example, jbg@acme.us.com. In an RFC 822 address, it is the part enclosed in pointed brackets.
This property returns an empty string if the name does not contain an RFC 821 Internet address.

In my mind this is just plain wrong.

XML entities when doing XSL transformations using LotusScript

I stumbled over an interesting post on developerWorks on external XML entities and XSL transformations in LotusScript: XSLT transform-ignoring external entity reference in R7.0.2 – but worked well on R6.5.4. I was puzzled by the post and looked a bit into it and was unable to find any real LotusScript API support for working with XML entities when processing XML in LotusScript.

I find it funny that there doesn’t appear to be any real API support for XML entities in LotusScript that I can see anyway. Hmmm – gotta go into Sherlock Holmes mode…