Object variable not set

update-smart-drivedb failing in Ubuntu 12.04

I've recently been having a couple of issues on a home server with an ssd drive, so I installed the smartmontools package using the excellent documentation on the Ubuntu Wiki.

So far so good. After enabling SMART on the drive in question as per the wiki, then running the test suite, viewing the results gave me a list of result with the following at the bottom.

Warning! SMART ATA Error Log Structure error: invalid SMART checksum

A bit of googling revealed that each disk has a somewhat unique SMART output which needs to be included in the smartmontools database. New disks are added to the project, and the local installation can be updated with the update-smart-drivedb command.

Running this command gave me another error

/usr/share/smartmontools/drivedb.h.error: rejected by /usr/sbin/smartctl, probably no longer compatible

A bit more googling lead me to this site, which has a reasonable explanation of the issue and a possible solution.

The update-smart-drivedb gets the latest drive information from sourceforge.net, but the layout of the site has recently change, breaking the update-smart-drivedb script.

The quick fix is to edit the file and change the url that is used to grab the updated drive configurations.

Using your favourite editor, open the file /usr/sbin/update-smart-drivedb and update the line

SRCEXPR='http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/$location/smartmontools/drivedb.h?revision=HEAD'

to

SRCEXPR='http://sourceforge.net/p/smartmontools/code/HEAD/tree/$location/smartmontools/drivedb.h?format=raw'


The update-smart-drivedb should now work correctly.

Back to working out if the disk is actually the problem...

Enabling the 3G Mobile Hotspot on a Motorola Milestone

Last year I purchased a Motorola Milestone and was reasonably happy using the free PDANet bluetooth tethering software. When Motorola finally got around to updating the system software to Froyo (2.2) earlier this year, I was keen to switch to the built in Wifi Hotspot functionality in this release.

Setup seems pretty simple, you enter the authentication type, SSID and a password and away you go. Whenever I tried this however, I always got the error message 'Mobile data service not available'.

As this always happened while I was away from a network connection, I have only just got around to fixing the problem. The solution is to make sure that the APN type is not set to the text 'default' (blank seems to work). This setting is found all the way down in

Settings >> Wireless & networks >> Mobile networks >> Access Point Names >> Your Access Point >> APN Type

After that the 3G Mogile Hotspot worked as expected.

Scripting HTML email in Domino

Two or three projects I have worked on over the last couple of years have required a way to generate HTML emails from a template that can be used to personalise the outgoing email. The technique that I have ended up using is more or less what I describe below.

The most reliable way I've found to create HTML email is by using the NotesMIMEEntity and NotesMIMEHeader Lotusscript classes to build a valid multi-part MIME message containing both images and text. The first step is to create the basic HTML email message that you wish to customise. Whilst you can build the email from scratch, a much easier way is to fire up your email client and create the email you wish to generate then send it to your Lotus Notes email account. This will give you the basic structure of the email from which to work from.

Example of a basic HTML email created in Gmail

Once you have recieved the email, we need to take a look at the source so we can pull out the multi-part MIME message that will serve as our template. In Lotus Notes, you can view the source of an email with the View >> Show >> Page Source menu option.

Viewing the source of a HTML email


The two parts we need to focus on are the message part with content-type text/html, and the linked image message part.

 --bcaec5016309e564e104a2d281f3
Content-Type: text/html; charset=ISO-8859-1
Hi Jon,<br><br>Take a look at this kitten!<br><br><img title="kitten.jpg" alt="kitten.jpg" src="cid:ii_12fd393a0baeba07"><br><br><br>Cheers,<br><br>Jon<br><br>
--bcaec5016309e564e104a2d281f3--
--bcaec5016309e564e304a2d281f4
X-Attachment-Id: ii_12fd393a0baeba07
Content-Type: image/jpeg; name="kitten.jpg"
Content-ID: <ii_12fd393a0baeba07>
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAPAAA/+4ADkFkb2JlAGTAAAAAAf/b
AIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoKDBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxsc
Hx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f
Hx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBJQGQAwERAAIRAQMRAf/EAKkAAAEFAQEBAAAAAAAAAAAA
AAEAAgMEBQYHCAEAAgMBAQEAAAAAAAAAAAAAAAECAwQFBgcQAAIBAwMDAgMGBAMGBAcBAAECAwAR
BCESBTFBBlETYSIUcYEyIxUHkaFCUrFiM8HhcoIkFtHxU2PwksJDNDUINhEAAgIBBAEEAQIEBQQD
AQAAAAERAgMhMRIEBUFRIhMyYQbwcYGhscHRMxTxQlIj4WIVFv/aAAwDAQACEQMRAD8A6MR184dj
jhEdHIBwj+FJ2APt2pcgFso5AIx/CjkA4JpSdgCI6XIGERmh2EOEdRdhhEetLkA/29KTsNDtmlJs
YthpSJiCUnYQSlKQDs0oGhBCPspSDHBL0SCCI6JGOEdLkAdlJsEEJSkYfbpyDF7dIQhHRI0H26JA

The second part of the message contains the encoded image of the kitten and is referenced by the html message part in the img tag.

The key to our solution is to use these mime parts as a template for our new emails, using the Lotusscript MIME classes to build a memo document. A complete solution is given in the sample database at the end of this post, but the key pieces of code are the following:

      'Do not convert MIME to rich text
    session.ConvertMIME = False
    
    'Prepare the mime root
    Set MIMERoot = docmemo.CreateMIMEEntity
    Set mimeHeader = MIMERoot.createHeader("Content-Type")
    Call mimeHeader.SetHeaderVal("multipart/related") 
    
    'Iterate through the list of Templates supplied, building child mime components as we go
    Forall template In strKeywordTemplates
         Call createMimeChildFromKeyword(template, docToEval, MimeRoot)
    End Forall
    
    'Cleanup
    session.ConvertMIME = True 'Set this back to true

The above code is part of the body of the main method that gets called in the sample database to create an email. We need to tell Notes to not convert our MIME Entities directly to rich text when they are created, then we create a MIMEEntity which represents our whole email (type 'multipart/related'). The code then itereates through the list of templates that hold the actual email content and adds the child MIME Entities to our root MIME item, MimeRoot.

 Sub createMimeChildFromKeyword(strKeyword As String, doc As NotesDocument, MimeRoot As NotesMimeEntity) 
     Dim viewTemplate As NotesView 
     Dim docTemplate As NotesDocument 
     Dim stream As NotesStream 
     Dim mimeHeader As NotesMimeHeader 
     Dim mimeChild As NotesMIMEEntity 
     Dim item As NotesItem      
     Dim intEncoding As Integer 
     Dim strContentType As String 
      
     'Grab the template document, we use this to populate the MimeEntity 
     Set viewTemplate = db.GetView("emailtemplates") 
     Set docTemplate = viewTemplate.getdocumentbyKey(strKeyword) 
      
     intEncoding = Cint(docTemplate.encoding(0)) 
     strContentType = docTemplate.mimetype(0) 
      
      
     'Now create a child to the passed in parent 
     Set mimeChild = MIMERoot.CreateChildEntity 
     'Create a stream to populate the MimeEntity 
     Set stream = session.CreateStream 
      
     'We need to take care of correct referencing for images/data and variable substitution for text 
     Select Case intEncoding 
           
     Case ENC_BASE64: 
          'Add a header and call it the same name as the keyword, so we can reference it from another template 
          Set mimeHeader = mimeChild.CreateHeader("Content-ID") 
          Call mimeHeader.SetHeaderVal("<"+docTemplate.Name(0)+">") 
          Set item = docTemplate.getFirstItem("rtvalue") 
          'Place the template value in the stream 
          Call stream.WriteText(item.Text) 
           
     Case ENC_NONE: 
          'Assume it could have tokens we need to evaluate against the document to fill in field values etc. 
          Call stream.WriteText(evaluateTemplate(docTemplate, doc)) 
     Case Else 
           
     End Select 
      
     'Fill the MIME Entity with the template data, setting correct encoding and mimetype 
     Call mimeChild.SetContentFromText(stream, strContentType, intEncoding) 
      
     'Close off the stream 
     Call stream.Close 
      
End Sub 
The createMIMEChildFromKeyword subroutine handles the creation of the MIME part representing the template that has been called (either the HTML of the email, an image, file or other encoded part). This code sets 'Content-ID' header to the template name, so we can reference templates from other templates (in our example, the image can be referenced from the HTML of the email).

The Encoding type of the template is used to determine whether we want to copy the template verbatim (as in the case of an image or file attachment), or we wish to apply the template against a notes document to populate some data in the email.

The evaluateTemplate function evaluates each component of delimited text against the passed in document. This allows the creation of 'mail merge' type emails.

To give it a try, create a copy of the sample database on your workstation, open in the Notes client and sign with your id file. Populate the sample data item email address fields with an email address you have access to, then try using the front end and back end email actions from this view.

HTMLEmail.nsf (512 Kb)

Ajax GET and POST requests in Domino

I've recently been working on a Domino based intranet application that makes use of Ajax to speed some queries and generally improve the user experience.

The Ajax functionality has been implemented many times by a number of different developers. This has resulted in a number of differing implementations of code to provide Ajax calls scattered throughout the system. Tasked with implementing some additional Ajax code, I thought I'd try to pull all of the Ajax functionality into a common code base.

Firstly, the use of HTTP GET and POST requests was a little erratic. Generally GET should be used for requests that don't change the server state (e.g. searches), whilst POST should be used for requests that do change the server state (e.g. writing to a document). This distinction allows for caching of results and warning the user of accidentally submitting a form twice for example. A GET request is easier to create on the client side using Javascript however, so this was the approach taken more often than not in the existing code.

On the browser side, I chose to use jQuery to handle creating the Ajax requests as this simplifies the browser specific issues and hides quite a lot of the complexity of Ajax behind sensible defaults. To use jQuery in a Domino Database, download the library and add it as a File Resource. I don't recommend importing it as a Javascript Script Library as it's quite large and can make the Domino Javascript compiler choke and refuse to save it. You can then reference it in the header of your form or page.



In the sample database linked at the bottom of the post, I have a simple html page with a field and button for creating a record, along with a field and button for querying the existing records. There is also an area for outputting the return values of both agents.

 <h2>JQuery Ajax Example</h2>

<div class="formLayout">
 <label>Name:</label><input id="RecordName" type="text" name="name" /> <br />
</div>

<input id="CreateRecord" type="button" value="Create Record" class="InputButton" />

<div class="formLayout">
 <label>ID:</label><input id="RecordID" type="text" name="name" /><br />
</div>

<input id="QueryRecord" type="button" value="Query Record" class="InputButton"/>

<div id="OutputPanel"></div>

After that it's a matter of binding event handlers to the page elements that you wish to invoke your requests (using their id attributes). The following code is in the JS Header of the example page. jQuery does use some confusing syntax for the uninitiated, but it makes selecting elements within the DOM much simpler. The jQuery website has plenty of documentation and the book jQuery in Action is also a valuable resource for getting up to speed.

//Get the name of the server/database
var serverArray = document.URL.split(".nsf/");
var servername = serverArray[0];
//Perform an POST request to create another record
function createRecord(){
var recordName = $('#RecordName').val();
$.post(
    servername+"/(createrecord)?openagent",
    { name: recordName},
    function(response) {
         $('#OutputPanel').text(response);
    }
);
}
//Perform a GET request to query a record
function queryRecord(){
var queryKey = $('#RecordID').val();
$.get(
    servername+"/(queryrecord)?openagent",
    { id: queryKey},
    function(response) {
         $('#OutputPanel').text(response);
    }
);
}
//Bind the two event handlers to their respective buttons
$(function(){
$('#CreateRecord').bind('click',function(event) {
    createRecord();
});
$('#QueryRecord').bind('click',function(event) {
    queryRecord();
});
});
The functions createRecord() and queryRecord() create a HTTP request using the jQuery methods $.post and $.get respectively. Both of these methods take as arguments, the URL to direct the request, an array of parameters for data and a callback function to execute once the request has completed. That's it on the client side!

For the Domino server side, I looked around the web and came across this excellent post from Karl-Henry Martinsson in which he details a script library with a single class, 'URL.Class' to handle both incoming GET and POST requests. I made a couple of small alterations to this class to handle single argument POST requests and to decode the parameters that have been encoded by jQuery, but it's pretty much included in the sample database as is.

The URL.Class script library abstracts away the parsing of parameters from HTTP requests, providing them as a list allowing the agent code to focus on the task at hand.

By constructing a new URL.Class object with the current session, then calling the GetParams() method you get a List containing all of the parameters from the HTTP request regardless of whether it was a GET or POST. Below is the code for the 'Create Record' agent.

Option Public
Option Explicit

Use "URL.Class"

Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim doc As NotesDocument
Dim url As New URLclass(session)
Dim params As Variant

On Error Goto errorhandler

'Plaintext response to ajax call
Print "Content-Type:text/plain" 

'Get the request values
params = url.GetParams()

'Initialise the view and create a new record document
Set db = session.currentDatabase
Set view = db.GetView("recordsbyid")
Set doc = db.CreateDocument

'Check to see whether we are being passed an empty name
If params("name") = "" Then
     Print "Name is empty, Document not created."
Else
     'Set the correct form and record id (naive implementation for simplicity)
     doc.Form = "record"
     doc.id = Cstr(view.AllEntries.Count + 1)
 
     'Add the passed in parameter for the name
     doc.name = params("name")
 
     'Save the document
     Call doc.Save(True, False)
 
     'Return some information to the client
     Print "Document ID " + doc.id(0) + " - '" + doc.Name(0) + "' Created successfully"
End If

Exit Sub

errorhandler:
'This will be outputed to the client if we have an issue
Print Error(Err) + " on line " + Cstr(Erl)
Exit Sub

End Sub

To give it a try, create a copy of the sample database on a Domino server and sign with an id permitted to run agents. Navigate to the URL of the database and try both the GET and POST actions from the default page. You should also see the documents being created in the database from the Notes client.

AJAXExample.nsf (512 KB)

Remove an entry from a multi-value field

A quick one-liner to remove a known value from a multi-value field in Lotusscript.

doc.FieldToChange = Evaluate(|@Transform(FieldToChange; "CurrentVal"; @If(CurrentVal="|+varToRemove+|"; @Nothing; CurrentVal))|, doc)

The above works for text values, the comparison will need to be changed for other types.

@Transform can be a great way to avoid off-by-one errors and the generally clunky @For syntax.

A colleague pointed out this handy notes.ini entry to enable logging of status bar messages to your notes log.

LOGSTATUSBAR=1

If you want to write to a local text file instead of your notes log, include the line:

Debug_Outfile=c:\temp\StatusBarLogging.txt

changing the file path to the desired location.

More info from the Domino 7 Admin help