Show-n-Tell Thursday

Using profile documents in a web application

A very quick tip for the non-admins out there today.

I am looking after an app which uses lots of profile documents on the web. It’s not the way I would choose to do things because whenever you change the configuration in a profile document it takes ages (up to half an hour) for the change to be consistently available over HTTP.

To get around that I found a new server console command which makes any change take effect immediately…

tell http clearcaches

It was new for me anyway, hopefully it will help someone else out.

Technorati Tags: Show-N-Tell Thursday

Computed For Display removes fields

It’s not Thursday I know, but what the hell.

OK, this is one which I am guessing that I should have known but I am convinced that Domino used to work in a different way. What’s the situation?

Well, we have a document which is edited on the web. The document has a field on it called “MyField”, the form which is used to display the document on the web has a computed for display field with the same name. We are finding that when the document is saved, that “MyField” is being removed from the document.

This seems entirely correct if looked at in isolation, computed for display fields are not saved. But the reason I am posting this (and am entirely expecting to get abuse for being such a dumb-ass) is that I am convinced that in previous versions (I am thinking in the R5.x timeframe) that the field would not be removed from the document, instead whatever was stored in the field would be used by the computed for display field regardless of the formula in the field on the form.

So am I just remembering incorrectly or has the way that Domino works changed at some point in the last few years?

It’s not a big issue, we just changed how the form was working, just a little unexpected.

Technorati Tags: Show-N-Tell Thursday

Automatically launching Javascript Debugger from code

I’m trying to locate a very odd javascript problem at the moment but not having much luck. However, in my browsings I have found a very useful little line which makes debugging a lot easier. If you enter the line “debugger” in your function then if you have the IE Script Debugger or the Venkman debugger installed then then will fire up when that line is executed and allow you to continue from that point onwards. Effectively you have the ability to set break points in your code, very useful I think you’ll agree.

So your code will look like this…

function foo() {
var bar=””;
debugger; //your script debugger will launch when this line executes
for (var i=0; i //Some code goes here
}
}

Technorati Tags: Show-N-Tell Thursday

Logging JavaScript Errors to OpenLog

If you haven’t heard of OpenLog then you should get over to OpenNTF and catch up now, don’t worry we’ll wait for you…

OK, are you ready now? Good then we can continue.

I have been busy adding OpenLog to a couple of applications over the last day or so, all works wonderfully (as we have become used to with Julian’s code!). However these apps are very JavaScript heavy and there are some problems which need to be sorted. The challenge is locating all of the errors out there, so I thought it might be useful to be able to log JavaScript errors to OpenLog as well as LotusScript and Java. It’s a little more involved than other implementations but my solution seems to work pretty well. (I know Kevin came up with a great logging apporach for when users do particular tasks, but I want to trap all unhandled JavaScript errors to make sure that we are fixing all of the underlying problems).

Javascript allows us to set the “onerror” function so that whenever there is a JavaScript error a function is run. What we are going to do is create an AJAX request which submits all of the details about the error to an agent which then logs the information to the OpenLog database.

Step 1
First we add two new JS libraries to the form header: “prototype-1.4.0.js” and “jserror.js”. Prototype is being used for the AJAX stuff (of course you can use your own method for the actual posting of data if you want to, it’s just we already have Prototype on every page anyway) and the new library called jserror.js:

function doError(msg,url,ln) {
  var strURLPrefix = document.location.href.split(“.nsf”)[0] + “.nsf”;
  var strURL = strURLPrefix + “/agLogJSError?openagent&rand=” + Math.floor(Math.random()*1001);
  var strData = “message=” + escape(msg);
  strData += “&url=” + escape(url);
  strData += “&ln=” + escape(ln);
  new Ajax.Request(strURL,
    {  method: ‘post’,
      postBody: strData,
      onComplete: handleErrorResponse
    }
  );
  return true;
}

function handleErrorResponse(originalRequest) {
  alert(“An error has been detected on this page and logged with the support team. Please continue to use the application as normal.”);
}

try {
  window.onerror = doError;
}
catch(er) {}

Step 2
Now we need to create the agent which errors will be posted to, it’s called agLogJSError and is really very simple:

Sub Initialize
  On Error Goto Whoops
  Dim sess As New NotesSession
  Dim dbCurrent As NotesDatabase
  Dim docContext As NotesDocument
  Dim vFields As Variant
  Dim vData As Variant
  Dim strMessage As String
  Dim i As Integer
  Dim url As String
  Dim errorline As String
  
  Set dbCurrent = sess.CurrentDatabase
  Set docContext = sess.DocumentContext
  
  Print |Content-Type: text/xml|
  Print ||
  
  strMessage = “Javascript Error:”
  
  vFields = Evaluate(|@URLDecode(“Domino”; @Explode(REQUEST_CONTENT; “&”))|, docContext)
  For i = Lbound(vFields) To Ubound(vFields)
    vData = Split(vFields(i), “=”)
    If vData(0) = “url” Then
      url = vData(1)
    Elseif vData(0) = “ln” Then
      errorline = vData(1)
    Else
      If strMessage “” Then
        strMessage = strMessage + Chr(10) + Chr(13)
      End If
      strMessage = strMessage + vFields(i)
    End If
  Next
  
  Call LogErrorJS (strMessage, url, errorline, SEVERITY_HIGH, docContext)
  
  Print ||
  
  Exit Sub
Whoops:
  Call logError()
  Print ||
  Print “Error #” & Err & ” ” & Error$ & ” ” & “Line #” & Erl & | in sub/function: “| & Lsi_info(2) & |”|+ “.”
  Print |
|
  Print ||
  Exit Sub
End Sub

Step 3
Finally to make things slightly easier I have created a new function in the OpenLogFunctions script library called LogErrorJS which just formats the error slightly differently than a “normal” LotusScript or Java error.


The end result of all of this is that when there is a JavaScript error the user gets an alert saying that an error has been encountered and logged with support but please keep going as it shouldn’t affect you. In reality you may not even want to notify your user that an error happened, I am undecided about that as yet, we’ll see what they think during testing.

To be honest all of this is terribly simple but I hope it demonstrates two things. Firstly JavaScript is a “proper” language and should be treated as such, ignoring error messages is not a good long term strategy. Secondly OpenLog is great but it’s also flexible, if it doesn’t do exactly what you want then just change it to meet your requirements, I don’t think Julian is going to mind too much ;o)

Big Reminder
Ooh, I almost forgot. If you’re using IE (spit!) and you’re a web developer then you probably have Script Debugging turned on. Well for the “onerror” function to work you have to have script debugging disabled. If you don’t then the function is just never called! Wonderful IE eh?

Online Demo: here
Download Demo Database: JSOpenLogDemo.nsf
Technorati Tags: Show-N-Tell Thursday

Getting information from a user's person document

OK this is a really simple little tip using an @Formula which I had completely forgotten about (hence the post really). The situation is that we have multiple cascaded NABs (or Directory Assistance directories or whatever the cool kids are calling them these days), the current user could have their person document in any of them depending on the user type. I was trying to do a lookup into each of the NABs to find a field on the person document, inefficient and stupid as the NABs are properly secured so the user can’t even do the lookup in the first place!

Enter the @NameLookup function which will go through all of the NABs on the server looking for you and return a specified field name like so:

@NameLookup([Exhaustive]; @UserName; "OfficeCountry")

I just love simple answers to problems. It’s just a little embarassing that I forgot about this one. Notes.net does still have its uses!

Technorati Tag: Show-N-Tell Thursday

Two Quickies

I haven’t done a SnTT post for a long time and I’m not really sure this qualifies as it’s just two bits of information…

Firstly get yourself over to Ben Poole’s site where the first beta version of DominoWiki 1.1 has been released for testing. Have a look and please log any issues so that we can get as stable a release as possible.

Secondly is just something that I recently learned about scheduled agents. It’s probably that this just passed me by and that everyone else already knows it, but did you know that scheduled agents can open databases on servers other than themselves in ND6 and 7? I didn’t, but it’s not something I have tried to do since the old R4 days when it wasn’t possible.

There, told you they were both quickies didn’t I!

Sorting categories by count

Yet more XML and AJAX, what can I say, it’s just so much fun playing around with this stuff.

While in Sweden we were talking about how a real “nice to have” would be to be able to get a list of categories sorted by the number of documents in each category. As has become patently obvious I am not a Notes client guy, but on the browser this is really pretty simple. There are two steps that we need to go through.

So what we’re aiming to get is a list of the categories that are in this blog and then sort them by the number of documents within each category.

Step 1 – Getting the data
This is where the AJAX comes in. Recently I have switched over to using prototype for all of my AJAX calls, it’s a really powerful (and free) javascript library which handles all of your AJAX needs and some more besides. You may have noticed the new “Now Playing” block over on the left which is using prototype to make a request every 5 minutes to see what I’m listening to in iTunes.

So to get the XML for the categories I do the following:

function getCategories()
{
var a=$H(
{
collapseview: “true”,
count: 1000
}
);
var myAjax = new Ajax.Request(
‘http://www.11tmr.com/11tmr.nsf/BlogByCategoryCount?readviewentries’,
{method: ‘get’, parameters: a.toQueryString(), onComplete: outputCategories}
);
}

To me this is why prototype is so great, I just need to give it a URL and some parameters and it will go and handle everything else for me including browser issues. The final URL we’re building here is:

http://www.11tmr.com/11tmr.nsf/BlogByCategoryCount?readviewentries&count=1000&collapseview=true

When the URL is loaded then prototype will call the “outputCategories” function and pass in the response from the URL (whether that be XML or HTML or text doesn’t matter at this point).

Step 2 – Sorting the Data
OK, now we have the XML of the view, one entry per category thanks to the “collapseview” URL parameter. The attributes of each entry tell us the number of blog entries under each category (the “children” attribute) and also the total number of documents for the category (i.e. the number of blog entries plus the number of comments as well) in the “descendants” attribute:

This next bit I can’t claim credit for, instead I pass on thanks to Breaking Par Consulting who posted a great tip about sorting multi dimensional arrays in javascript. The pattern is to create a class function, in this case it has three properties – Category Title, Count and Number of Comments. We loop through the XML and create a category object for each category and place the new object into an array. After the array has been populated it’s just a matter of calling the sort method but making sure that you give it a function to define the sort rule:


for (i=0; i {
cat = getInnerText(results[i].getElementsByTagName(“entrydata”)[0]);
count = parseInt(results[i].attributes[2].value, 10);
comments = parseInt(results[i].attributes[3].value, 10) – count;
aCats.push(new category(cat, count, comments));
}

aCats.sort(sortCount);

function category(title, count, comments)
{
this.Title = editReplace(title, “\n”, “”);
this.Count = count;
this.Comments = comments;
}

/*
sorting pattern courtesy of Breaking Par
http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256C8D00514FA4
*/
function sortCount(a, b)
{
var x = a.Count;
var y = b.Count;
return ((x > y) ? -1 : ((x }

The Result
Hopefully that makes sense. If not then go and have a look at the final result and follow through the whole code there.

XML Encoding “European” characters

So while in Sweden one of the things I have been working on required me to output some XML from a view. I have a standard function which I use to encode text so that it will work correctly. But I learned two things which may well be blindingly obvious to you but were new to me…

Firstly, if you’re using a European character set which has lots of high number ASCII characters in then you want to be using ISO-8859-1, not UTF-8, as your XML encoding style:

<?xml version="1.0" encoding="ISO-8859-1" ?>

Second, where I had just encoded the basic 5 characters in the past: <, >, ", ‘ and &, you also need to translate any character with an ASCII code above 127 to it’s XML representation. So my new XMLEncode function looks like this:

Function XMLEncode (TextData As String) As String
    Dim MyChar As String
    Dim F_XML As String
    Dim i As Integer
    
    F_XML=""
    For i=1 To Len(TextData)
        MyChar=Mid(TextData,i,1)
        Select Case MyChar
        Case "<"
            F_XML= F_XML & "&lt;"
        Case ">"
            F_XML= F_XML & "&gt;"
        Case |"|
            F_XML= F_XML & "&quot;"
        Case "’"
            F_XML= F_XML & "’"
        Case "&"
            F_XML= F_XML & "&amp;"
        Case Else
            If Asc(myChar) > 127 Then
                F_XML = F_XML & "&#" + Cstr(Asc(myChar)) + ";"
            Else
                F_XML= F_XML & MyChar
            End If

        End Select
    Next
    XMLEncode=F_XML
End Function

PS: I have only changed the bold lines above, I don’t think I wrote the rest of the function but I can’t remember where i got it from so if you recognise it please let me know and I’ll add your credits!

Show and Tell Thursday – Using gmail as a spam filter

No code this week, no time what with the release this morning and washing machines blowing up earlier in the week.

So instead a tip. It’s something which I have mentioned to a few people and they seemed to think it was an interesting idea for those of us running home Domino servers for email.

In the control panel application of my main mail domain I have it set up to forward all of the mail received to be forwarded on to my gMail account. In gMail I then have it set up to forward all mail that it receives on to a different, undisclosed email address which is hosted on my home server.

So why would I do this? Well there are two main reasons…

1) I don’t need to worry about spam filtering as gMail handles that for me. gMail doesn’t have the best spam filtering in the world but it’s getting better and it will only forward on mail which it doesn’t think is spam, so my home Domino configuration can remain nice and simple.

2) I have a backup of my mail for when my home server is not accessable during network outages or, in the worst case, if the server dies completely.

There are downsides to this approach, all mail that I receive has the flag to identify suspicious mail in the new ND7 mail template, but I have just hidden that column in my Inbox so it doesn’t bother me. When I’m sending mail from the Notes client, to make it come from my "real" email address I need to have my Location document set with the correct "Internet mail address".

Other than that it has been a really successful set up for me over the last few months.

Now all you admins out there can start to pick holes! (Actually I am serious about that, if there are obvious flaws which I have missed I’d love to know them please).

Show and Tell Thursday – Rational Test Manager “rtpar” decoder

Joining in the spirit of Rocky’s Show and Tell Thursday idea, I thought I’d share a tool which I wrote last year. The reason I haven’t posted it before is that it’s got a fairly niche market and it’s just re-using techniques which are pretty familiar now (AJAX to get some XML and then XPath and DOM processing to output some HTML). However it’s been pretty useful for me and I don’t have a huge amount of time to write anything new at the moment so you never know.

So what is it? Well, if you use Rational Test Manager for writing your test scripts you may know that getting at the scripts themselves can be quite hard work and pretty much impossible unless you have the Test Manager software installed. A part of our release procedure is that we provide the System Test scripts to the business users to check over to make sure that they reflect "real" usage of the system. So they don’t have access to Rational and they wouldn’t make much use of the other features anyway, all we want is a listing of the steps in each script. To get this out manually into text files takes ages. However, there is an export feature which will give you an "rtpar" file which contains all of the scripts within a project. Now if you look at contents of the rtpar file you’ll find that it’s just XML so where we are going next is pretty obvious…

I put together a (very) quick NSF which has two forms. One allows you to upload the XML file (with a standard Domino server you just need to change the rtpar file extension to xml so that Domino knows what it’s serving out), the other looks at the file you uploaded and formats it into HTML so that you can print it out or do whatever else you like with it.

You can see a demo of the app here.

Now, the obvious first comment is… Why aren’t you using XSLT to produce this? Well there’s no reason I couldn’t, but I needed it quickly and frankly I know the AJAX stuff better than XSLT. So just get off my back man 🙂

The code itself is pretty simple, all it does is work out the URL to load the XML file using the ubiquitous XMLHTTPRequest. Once the XML is loaded it’s really a matter of just looping through each test folder and within those each test script and outputting the contents into a table format.

The sample I have pointed to above is very small, just a single script, but we have used this tool to produce a PDF for our users which amounts to several hundred pages of scripts. Although it takes a while for the XML to load (about 30 seconds or so) it still has no problem in parsing it and displaying it.

In conjunction with the CutePDF printer tool it has saved my testers and me hours of work for every release we do. So I hope it’s of use to someone out there.

Download the NSF here