Project 1d
Project 1d - Hello World Portal Module
A simple task - a little knowledge of Java is helpful ![]()
In this example, we will create the ‘Hello World’ portal module from scratch using a Java IDE. Once we have the basic structure, we will add a globe icon to remind the user what the world looks like, and then expand this to allow them to set their preferred view, from a choice of three globes:

This provides us with an example of a building block which uses CustomData and introduces the step and dataElement entries in the bbUI tag library which are used on the edit pages. At the end of this project you should have a user-customisable tool which you could expand for your own purposes.
Difficulty
Pretty Easy - no harder than spelling the word ‘arithmetic’… …backwards!
Requirements
You need to be running Blackboard with the Community Portal
You need to install the NetBeans IDE onto a PC with a lot of free disk space (NetBeans is a whopping 130MB download with the latest Java SDK, which unpacks to a sizeable 450MB on your disk when it is installed)
You need some files from your Blackboard Server
It would help if you joined the BBDN to get the Building Blocks SDK - the (partial) documentation
Time
A couple of hours - depending how long it takes you to download the files across the web!
Summary
1. Download and install NetBeans 4.1 and the latest Java Development Kit from http://java.sun.com/j2se/1.5.0/download.jsp
2. Join the Blackboard Developer’s Network (BBDN) if you aren’t already a member - note for many people this will be free
3. Download the Blackboard SDK which matches your current version of Blackboard from the BBDN and unzip the files to a directory on your hard drive (e.g. c:\data\bb)
4. Copy some JAR files (bb-platform.jar and bb-taglibs.jar) from your Blackboard server to the same directory on your PC
5. Create a directory on your PC to save your NetBeans projects in (e.g. c:\data\netBeansProjects)
6. Start netBeans and create a new project HelloWorld1d, which we configure for Building Block development (e.g. using Apache Tomcat and the Jakarta Source Structure)
7. Create a netBeans library which contains the Blackboard JAR files and the zipped up documentation
8. Alter the project properties so that it doesn’t package the Blackboard JAR files into our building block and also, to test compile our JSP pages
9. Create a module folder, and add a page view.jsp
10. Edit web.xml (created for us by netBeans) to include the Blackboard tag libraries
11. Now create config and taglibs folders in WEB-INF
12. Put the Blackboard tag library files into the taglibs folder
13. Create the code for view.jsp using netBeans
14. Create the manifest file bb-manifest.xml
15. Create an images folder and add three GIF files
16. Use the class blackboard.platform.plugin.PlugInUtil in view.jsp to find the URL of the image we want to display on view.jsp
17. Modify the manifest file, adding an <edit> entry
18. Create the page edit.jsp which will use the <bbUI:modulePersonalizationPage> and <bbUI:step> tags to give it a standard Blackboard look and feel
19. Make a call to blackboard.portal.external.CustomData to retrieve any previously stored preferences
20. Create a form which allows the user to alter the value of the image to display
21. Create the page proc_edit.jsp which processes the user’s choice and updates the CustomData
22. Finally, alter view.jsp so that the image shown depends on the value set in the CustomData
23. Upload the building block to your server and check it works!
Step by Step Instructions
This project is bigger than the previous three and so has been broken down into four discrete steps: installing netBeans, getting the Blackboard files, configuring netBeans and finally, creating the building block. Whilst this may seem like a lot more work than project 1c, remember that most of this has only to be done once.
Section A: Installing netBeans

1. The first task is to get hold of a copy of NetBeans and the Java Development Kit Version 1.5 (also known confusingly as Java version 5.0) - from this URL:
http://java.sun.com/j2se/1.5.0/download.jsp and save it on the desktop.
Most users should select the option to download the two items bundled together NetBeans IDE + JDK 5.0 - if this is your first dalliance with Java, please choose this option.
If you already have an up to date JDK installed, you could opt to just download NetBeans from the NetBeans website. Note that simply having the latest JRE (Java Runtime Environment) on your computer is not enough. A JRE allows you to execute Java code on your computer, but it doesn’t allow you to write or compile it. For this, you need the JDK.
Java Versions
The Java language is continually being updated. The sharp eyed amongst you may have noticed that when I was writing this, the latest version of Java was 1.5.0.6. Later updates will probably be added by the time you try, so you may see 1.5.0.7 or something similar. In a perfect world, you should be developing your code using exactly the same version of Java that sits on your Blackboard server. If they are both broadly the same, e.g. 1.5.0.something, then you are probably OK. If not, try and get a version of Java that matches the one used on your Blackboard server - take Mohammed to the Mountain as it were.
The projects here used NetBeans version 4.1, though later I switched to version 5 (and now version 6 is in Beta). I know this works for netBeans version 4.1 and 5.5. I’ve not yet had a chance to play with 6).
NetBeans is itself a Java program, and so there are versions for Windows, Linux and Macs. Make sure you get the correct version for your operating system. For most of you, I guess that will be Windows.

Read the License agreement from Sun, and assuming you agree to the terms, download the software.
Why is it so big?
The download may seem enormous, but there is a reason for this. You are getting:
- The latest Java runtime files (needed for running Java on your computer)
- The latest Java Development Kit - the JAR files, compiler and Javadocs you’ll need to write Java code
- The NetBeans program itself
Plus a whole bundle of other packages and programs, that you may not think you need, but as anyone who has installed Eclipse will testify, you do need to carry out Java development. The nice thing is that NetBeans sets them all up for you- a real boon for novices like me ![]()
You also get:
- Tomcat - your own web-server
- Ant - a scripting language that NetBeans will instruct to automatically compile your code and package it up nicely as a WAR file ready for installing on your Blackboard server
- JUnit - software you can use to test parts of your code to make sure it does what you expect it to do
There’s probably other things too that I’ve not come across yet…
2. Begin the installation process by clicking on the file you downloaded.

It will have a catchy name something like this:
jdk-1_5_0_06-nb-4_1-win-ml.exe
You should see a message saying that the Java Virtual Machine is processing
When the install process begins properly, you should see a box like this confirming what you are about to install:

3. Click the Next > button
You then see a page showing you the license agreement - NetBeans and Java are free for certain uses, but only if you agree with the conditions displayed. Hopefully you can:
4. Click the Accept radio button and then click Next >
You are then asked where you want to install NetBeans (and the Java SDK assuming you downloaded this too). You can probably accept the defaults, though the more paranoid/battle-scarred may want to choose an alternative location that avoids spaces in the filenames.

5. Make any changes if you want by browsing to the appropriate directory, then click the Next > button
NetBeans displays a page saying that it is now ready to install.
6. After you have got over the shock that it occupies around 450MB click the Next > button then go and make yourself a cup of your favourite beverage whilst it installs…
All being well you should return to a message saying that it has successfully installed the software:

7. click the Finish button to exit. You should see a new shortcut to NetBeans has appeared on your desktop:

Don’t click on this immediately, first take a minute to decide where you want to store all your NetBeans projects. This doesn’t need to be a subfolder within NetBeans, so if you can’t resist the calls of that siren of your PC - the My Documents folder - it is OK to create a new folder here called netBeansProjects or something.
Personally I opted for the shorter:
c:\data\netBeansProjects
8. Create an appropriate folder.
9. Now reward your abstinence, by clicking on the shortcut to start NetBeans. You will see a dialog appear whilst it loads files, which can take a few seconds:

10. All being well, you should see NetBeans complete this process and display the blank screen which, if you take five steps back from your PC and squint looks a bit like this:

Congratulations, you have installed NetBeans. We will return to the program later to start writing the code. If you can’t wait, you might want to explore the NetBeans Quick Start Guide or the tutorials available from the Help menu.
Section B: Getting the extra Blackboard-specific files

Before we get too carried away though, we need to get some Blackboard-specific files. The NetBeans bundle we have just installed contains the full set of Java classes, but it knows nothing about Blackboard. To remedy this, we need to download more files - some that we will refer to directly, and others we will use to create a Library in NetBeans, to help us write and then compile our building block.
There are four types of files we need:
- The compiled Blackboard Java code, which is available as a series of JAR files, mostly we will just need bb-platform.jar
- The tag library files (such as bbUI.tld, which we met in project 1c) which help us create pages with the Blackboard look and feel. These are actually zipped up inside bb-taglibs.jar
- The JavaDoc - standard documentation, to help us write the code
- PDF Developer Guides to help take some of the guesswork out of building portal modules and tools
You can get items 1. and 2. from your Blackboard server, but to get 3. and 4. you used to have to to join the BlackBoard Developers’ Network. Membership is currently free to members of academic institutions who have already shelled out for an enterprise license. If you qualify for free membership, ask your Account Manager for a login without delay. As a member of the BBDN you can create accounts for 24 of your colleagues, so there should be no arguing about who can log in
Update
Alternately you can now download them all in one handy place - eduGarage. If you’ve not been there yet it’s worth a look. You can get the SDK and the PDF guides from http://www.edugarage.com/display/BBDN/downloads

11. To access the BBDN once you have an account log in at: http://behind.blackboard.com/b3/developer/
Once you authenticate, you will see a standard opening page - hopefully like this:

If you already had an account (e.g. if you are a System Administrator) then Behind the Blackboard may not open by default in “developer” mode. Switch to Developer mode by clicking on the link with the same name in this box:

12. The SDK for Blackboard 6.3 (and earlier) can be downloaded from the Reference Center. The last time I tried, this was the URL to use: http://behind.blackboard.com/b3/developer/refcenter/61/
As I am running 6.3. I chose the 6.3 SDK (which came with .Net information too). If you are running an earlier version, pick the appropriate version instead. If you are working on version 7, use this link: http://behind.blackboard.com/b3/developer/refcenter/as7/
13. Save the zip file somewhere temporarily, the desktop will do for now:

14. Double click on this icon to see what it contains. This is what I found:

It contains Developer Guides in PDF format, the JavaDoc format documentation which lists and explains the various public Blackboard classes and methods (zipped up) and to sample applications. I leave their installation and subsequent dissection to you…
15. Extract the files to a suitable location. I used:
c:\data\bb
I then deleted the .Net files, as I don’t (can’t) develop in this language. You might want to have a look at them in your own time.
Bedtime Reading
Blackboard provide a range of documentation to help the budding developer. For this project, you are strongly advised to have a look at these two:
The Blackboard Building Blocks Module Developer Guide (which should be called something like bbas_r6_3_mod_dev.pdf)
The Blackboard Building Blocks Developers Guide (which should be called something like bbas_r6_3_developer.pdf)
Explore the others later too.
Now we need to get the JAR files from your Blackboard server, so that we can use the Blackboard-specific Java classes in our code.
16. The easiest way to get these files is via ftp. If you don’t know how to do this, consult a friendly System Administrator. Where to find them depends on the operating system you run Blackboard on:
On a Linux box, copy these files:
/local/bboard/blackboard/systemlib/bb-platform.jar
/local/bboard/blackboard/systemlib/bb-taglibs.jar
You don’t need this file yet (it includes the Content System APIs and the Event APIs) but it will come in handy for later projects. You may as well download it at the same time (particularily if you are troubling someone else to get them for you):
/local/bboard/blackboard/systemlib/bb-cms-admin.jar
You also need the tag library files we used in project 1c:
bbData.tld
bbUI.tld
bbUI.xsl
Luckily these are zipped up inside a file we have already got: bb-taglibs.jar - so unzip them using WinZip or a similar program. They also used to be found on your server, but it seems this stopped happening from version 7.x onwards. It’s safer to use the version in the JAR file - you know they are up to date.
Thanks to Gregor Bowie I can now tell you where to find them on a Windows box:
Go to your install drive (e.g. C:) and they should be here:
C:\Blackboard\SystemLib
Save them all in the same folder you put the files from the Blackboard SDK in step 15, in my case this was:
c:\data\bb
Section C: Setting up netBeans for building block development
17. We are finally ready to begin. If NetBeans is not still running, double click on the desktop shortcut to view the opening page - Welcome in the Source Editor window (usually on the right hand side). This offers some shortcut buttons for first time users, but we don’t need any of that. Close this page by clicking on the cross next to the Welcome page’s title:
18. Instead we will create a new project by clicking on the File menu and choosing the option New Project… - the following dialog should open:
19. As we are going to create a building block that is used over the web, it should be no surprise that you should click on the Web category and then select the Web Application project, before clicking on the Next > buttonAnother dialog is shown - don’t be put off by the number of options, as NetBeans remembers your choices from project to project, so you only need to think hard about them once:
Fill in these fields:
- Project Name - this is used to identify your project and also to give the name of the final WAR file you will create to add your building block to the server. As such the name can’t contain spaces or any exotic punctuation. I suggest typing:
HelloWorld1d
- Project Location - is the directory in which all the code you create is stored, plus the packaged WAR file ready for distribution. Use the directory you created specially for this purpose in Steps 7-8. I chose:
c:\data\netBeansProjects
- Project Folder - this is the NetBeans file which stores information about your project. You should find this box is populated automatically by adding the Project Name onto the Project Location so don’t change this
- Source Structure - determines which implementation of Java NetBeans will use to help you in your coding. The choice is between Java BluePrints and Jakarta. As we are creating a building block which will be deployed on an Apache server, and Apache is developed by the Jakarta Foundation, we should choose:
Jakarta
- J2EE Version - this indicates which version of the Java Enterprise Edition we want to develop our code for. It really only applies to people using the Enterprise Edition of Java (JEE). We, in the cheap seats, are using JSE (Standard Edition). As such, we can ignore it and leave it as:
J2EE 1.4
- Context Path - this tells NetBeans where to look for files within the project. It is prepopulated with our Project Name so leave it as:
/HelloWorld1d
- Server - this tells NetBeans which server you should use to compile and display your code. Unless you downloaded the version of netBeans that comes bundled with Sun’s Enterprise Server, you will have no choice here. Luckily the option is the one we want:
Bundled Tomcat
- Use JDK 1.4 and Set Source Level to 1.4 - this tells NetBeans which version of the JDK to use. This can be important when you don’t know what version of Java other people who want to use your code will be running, as you can force NetBeans to restrict you to features of the widely used version 1.4 of the language. However we do know - Blackboard specify the minimum version needed to run their software (and if you are just developing for yourself, you know exactly which version is on your server). For Blackboard 6.3 and beyond, you need Java 1.5 so let’s not miss out on the chance of using some of the improvements packaged in Java 1.5:
Untick this box
- Set as Main Project - NetBeans allows you to open more than one project at a time (handy if you want to re-use code) but it needs to know which one to compile. The answer is the one you have currently got set as the Main Project. If you don’t declare this now, it will prompt you politely for it later. To save yourself some blushes, set it now:
Tick this box
Then click on the Finish button to open your new project:

NetBeans opens, note it has created some folders for us in the Project panel to the left.
20. Expand the folder called Web Pages and then the two subfolders WEB-INF and META_INF by clicking on the plus icon to the left of the folder graphic. You should now see a list like this:

If we compare this to the structure of the files we created in project 1c:

… we can see that there are some similarities: both have a WEB-INF folder containing a file called web.xml
Shortly we will add more folders and files to the project. Note that this time we don’t need to create the web.xml file from scratch - netBeans has done this for us (using the Jakarta web.xml conventions, because we told it to use these when we set up the project). Life is beginning to get easier
NetBeans has tried to be even more helpful by creating a new JSP file for us - index.jsp. It uses this name, as an index page is the default page that a web server will try and display if you don’t specify an exact page in your URL (be it index.htm, index.html, index.php, index.jsp, or any other variant) . This blog is an example: to get started, you probably typed “http://www.dur.ac.uk/malcolm.murray/blog” into your browser. The Durham webserver, however, actually served you the page “http://www.dur.ac.uk/malcolm.murray/blog/index.php” as it should. For building block developers, it is a case of “Netbeans, thanks, but no thanks”. We will not be needing index.jsp, and anyway it is in the wrong directory for us, so let’s tidy things up as follows:
21. Close the file index.jsp by clicking on the cross to the right of it’s name.
22. Now delete it permanently from this project by right clicking on the words index.jsp in the Project window to the left and choose the option Delete from the menu:

23. A dialog box appears - press Yes to confirm that this file will be deleted.
Now we need to set up the Blackboard files (the compiled classes in the JAR files and the HTML documentation in the zipped JavaDoc files). We will create what NetBeans calls Libraries that keep the classes and documentation together. This is a task you only need to do once - in future projects we can just import libraries created in other projects.
24. Right click on the project icon HelloWorld1d in the Project window and choose the option Properties from the bottom of the menu:

25. Click the Libraries category in the dialog box that appears. As you can see, currently we have no additional libraries in this project:

26. Click the Add Library… button to display this dialog listing currently available libraries. Currently it only lists the three that come with NetBeans:

We need to add new libraries with the Blackboard materials:
27. Click the rather unintuitively named Manage Libraries… button to open the Library Manager page:

28. Click the New Library… button at the foot of the page, to finally display a dialog that allows us to create a new library:
NetBeans developers take note - surely this navigation route could be shortened!

29. Enter a name for your new library, then press the OK button. As you will see if you try, NetBeans will tell you that this name cannot contain spaces or most punctuation characters. This library will contain the standard APIs used for creating Building Blocks, so I suggest calling it:
BuildingBlocks
A new entry ‘Building Blocks’ appears in the Library Manager. It should be highlighted. If it isn’t, click once on the name to highlight it like this:

We now have a name for our library, but nothing in it. NetBeans likes you to add the compiled code first on the Classpath tab, then any documentation (if you have it) via the JavaDoc tab. As Blackboard haven’t released the Java source of these classes, we have nothing to add to the third, Source tab. We will add two JARS to this Library. The first is bb-platform.jar which contains the common APIs. We will also add bb-taglibs.jar, as this will help NetBeans parse any reference we make in our code to the tag libraries bbUI and bbData we used in project 1c and will be using again here.
30. With the Classpath tab at the front, click the Add JAR/Folder… button to see a standard file browsing dialog:

Navigate to the folder in which you saved the Blackboard files in step 16 and select bb-platform.jar and bb-taglibs.jar. You can hold down the Ctrl button to select both files at once (as shown here), or add them one at a time, as you see fit.
31. With the JAR file(s) selected, click the Add JAR/Folder button
32. Back in the Library Manager window, click the JavaDoc tab and then click on the Add ZIP/Folder… button:

33. Select the zipped up documentation for the Building Blocks API - which should be called something like 6.3.1.382_building_blocks_api_spec.zip - then click the Add JAR/Folder button. Note there is no documentation file for the tag library (instead you have to try and glean their function from the documentation within the individual tld (tag library description) files in your Building Block - more on this later).
34. Click the OK button to close the Library Manager dialog. We have added our Library. (In a later project we will create another library for the Content System and Admin/Event APIs).
35. With our new library BuildingBlocks still highlighted, click the Add Library button to add it to this project:

You are then returned to the Project Properties dialog:

Note that currently there is a tick in the column ‘Package’ next to this library. This tells netBeans to include the necessary JAR files in the WAR file we will build to upload our Building Block. In this case this is not a good idea, as we know that these files will already be present on the server. Thus we do not want to add them to our WAR file, so:
36. Remove the tick in the Package column for our BuildingBlocks library, by clicking once on the checkbox.
Before finishing, this is a good time to check the compiler settings.
37. Click on the category Compiling in the Project Properties dialog, to open this dialog:

38. Tick the box marked Test compile all JSP files during builds
This is a useful feature of netBeans - not all IDEs allow you to compile your JSP files as well as your Java classes. It helps you trap and fix errors before you have uploaded your WAR file onto the server.
39. Click the OK button to close the Project Properties dialog
You may see a dialog displayed whilst netBeans scans the new libraries to prepare it for code assistance.
Finally we are ready to write some code
Section D: Creating the building block
As we are creating a portal module, convention dictates that the relevant files should be stored in a folder called module. We don’t have one yet, so let’s create it now:40. Right click on the folder icon Web Pages in the Project window and choose the option New from the first menu and Folder… from the sub-menu:
41. Use the dialog that opens to name the folder:
module

42. Click on the Finish button
Now we want to create our new JSP which will be displayed by default. Convention dictates that we call this view.jsp:
43. Right click on the folder icon module in the Project window and choose the option New from the first menu and JSP… from the sub-menu
44. A dialog opens similar to the one in step 41, except this time it is prompting you for the file name. NetBeans will add the appropriate file ending automatically (in this case ‘.jsp’) so simply name the file:
view
45. Click on the Finish button to see the new file open up
The file opens in netBeans with a standard page of example code. Later we will replace this with code that matches that used in project 1c which looked like this:

Note that it uses calls to the tag libraries bbData and bbUI. As before, we have to edit web.xml to tell the server where to find these:
46. Double click on the icon web.xml in the WEB-INF folder in the Project window. This will open it for editing in netBeans:

The file opens in ‘General’ view which helps you write code. We want to switch to native XML view to allow us to edit the file directly (for speed).
47. Click on the word XML in the tab bar at the top of WEB-INF to see the raw code:

48. Highlight these lines which are used by the web server in a normal JSP web project to tell it what page it should display be default:
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
</welcome-file-list>
The code is coloured to help you identify the different parts. Tags are in blue. Values that you need to supply are shown in black.
49. Press the Delete key to remove the entire <welcome-file-list>… … </welcome-file-list> entry. In a Blackboard building block, we set this elsewhere (using the bb-manifest.xml file)
Note that after you deleted the text an asterisk appeared next to the filename web.xml in the Source Editor window. This indicates that you have made changes to the file, but not yet saved them.
50. In the gap where they were type in details about our Blackboard tag libraries (this code is exactly the same as that used in project 1c:
<taglib>
<taglib-uri>bbUI</taglib-uri>
<taglib-location>/WEB-INF/config/taglibs/bbUI.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>bbData</taglib-uri>
<taglib-location>/WEB-INF/config/taglibs/bbData.tld</taglib-location>
</taglib>
Code Completion
Note how netBeans colours your code as you type it. If you pause part way through, netBeans can help you by completing entries. E.g. if you have typed:
<taglib>
<taglib-uri>bbUI</taglib-uri>
<taglib-location>/WEB-INF/config/taglibs/bbUI.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>bbData</
NetBeans will close the tag for you on this line:
<taglib-uri>bbData</taglib-uri>
More impressive, now press the return key to move to the next line. If nothing happens simply type the opening left bracket:
<
NetBeans should then offer you a list of appropriate tags:

Use the arrow keys to select the correct entry then hit return to insert the code.
As you will see if you are a slow typer, where there is only one choice that fits, netBeans will add it automatically for you. Once you get used to this, it can be a great help. You may be asking how it knows what to offer. It is looking in the BuildingBlocks library that we set up earlier.
51. With the changes made, click the SaveAll button on the toolbar to save these changes:

The asterisk disappears from the Source Editor window.
You before you close web.xml - look at the icon. You may notice a small red cross has appeared next to this file. This indicates that netBeans has detected a problem. If you hold the cursor (mouse) over this cross, it will show you the problem as a tool tip:

The problem seems to be with our changes - the taglib entry is not part of the standard Java library that netBeans is using to check our code. It doesn’t know about the Blackboard additions. We could just leave this - the Blackboard server will understand it, but I’m sure it would upset some of you. We will sort this, by altering some of the code in the header. Don’t worry too much about this, just think of it as swapping rulebooks
52. Replace these lines in web.xml:
<?xml version=”1.0″ encoding=”UTF-8″?><web-app xmlns=“http://java.sun.com/xml/ns/j2ee”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd”
version=“2.4″>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
with:
<?xml version=”1.0″ encoding=”UTF-8″?><!DOCTYPE web-app
PUBLIC “-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN”
“http://java.sun.com/j2ee/dtds/web-app_2_2.dtd”>
<web-app>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
53. Click the saveAll button to save your changes.
You should notice with some satisfaction that the red cross has disappeared - netBeans accepts our code! ![]()

54. Click the cross icon to the right of the file’s name (web.xml) in the Source Editor to close the file.
We have made reference to some the Blackboard tag libraries in the file web.xml, but the folder we specified in the <taglib-uri> location does not yet exist. If we try and make reference to them in a file such as view.jsp then netBeans will report errors (saying it can’t find the files). Worse, were we to try and install the building block on a Blackboard server it would fail. Lets add the files before we forget:
55. Repeat the process in steps 40 to 42 to create first a new subfolder of WEB-INF called config (but begin by right clicking on the WEB-INF folder) and then a new subfolder of config called taglibs (so here, right click on the config folder).
At this point I must confess that I can’t find a way to add the actual files using netBeans.
Thus we have to temporarily minimise netBeans and copy the files into our project using Windows Explorer or a similar program.
56. Copy these files:
bbData.tld bbUI.tld
bbUI.xsl
to the folders you created in step 53 above. In my case this is:
C:\data\netBeansProjects\HelloWorld1d\web\WEB-INF\config\taglibs\
Return to netBeans quickly before anyone notices!
It is time to work on the page view.jsp, you might find it useful to refer to the completed code sample used in project 1c (which is shown above step 46) as we build up this page. The file view.jsp should already be displayed in the Source Editor. If it’s not, locate it in the list of files in the Project window down the left hand side, and double click on it:
57. Highlight the default text in view.jsp and delete it all to leave you with a blank page
58. Add the following two statements at the top of the file so that we can make use of the Blackboard tag libraries:
<%@ taglib uri=”/bbData” prefix=”bbData” %>
<%@ taglib uri=”/bbUI” prefix=”bbUI” %>
Your file should now look like this:

59. Now add the opening and closing <bbData:context> tags so that we can establish who the user is:
It is suggested that you type these in rather than cutting and pasting them from your browser, so you can see netBean’s code completion at work
<bbData:context id=”ctx”>
</bbData:context>
60. Within these tags add the opening and closing bbUI:docTemplate tags so that the page contents display correctly:
<bbUI:docTemplate title=”">
</bbUI:docTemplate>
Note how netBeans helps you by offering fields such as title when you are building the docTemplate tag. When we add the closing tag, the opening tag is briefly highlighted in a fetching shage of pink.

Note also how we built up the code - by adding pairs of opening and closing tags one pair at a time, rather than simply typing the final code in line by line. By opening and then closing tags quickly, netBeans is able to provide us with better code completion suggestions.
61. Now type a pair of Java code tags in between the <bbUI:docTemplate> tags:
<%
%>
We want to get the user from the context so…
62. Between these Java tags type:
User user = ctx.getUser();
We need to tell the server what a ‘User’ is. If we were writing a simple Java class, netBeans would already be flagging this line as an error, as there is no object called User in any of the standard Java Libraries. We need to add an import statement pointing at a class within bb-platform.jar:
63. At the very top of the file add a new line and use netBeans to help you add the following:
<%@page import=”blackboard.data.user.User”%>
As you start to type ‘<%@’ netBeans should offer you a range of options including the one you want: ‘<%@page’ and it will automatically add the closing ‘%>’. Type a space between the tags and you will be offered a list of options with an explanation. Amongst them is ‘import’ - the one we want. If you select this and press return it also adds ‘=”"‘. Within the quotes if you type ‘blackboard.’ and then wait (or press Ctrl and the space bar together) you should see a list of possibilites at each step, to help you choose the right class. If you got as far as ‘blackboard.data.’ before pausing, the code completion suggestions should look like this:

You are seeing a list of possible packages (we want ‘user’) and below this an excerpt from the relevant JavaDoc, if it is available.
Continue until you have the full entry: blackboard.data.user.User
64. Below the Java tags add the code to say Hello to the user. In full the code looks like this:
Hello <%=user.getGivenName()%>
Pause after you have typed
Hello <%=user.
Your screen should look like this:

Note how netBeans is doing two things:
- The red cross two thirds of the way down the page indicates an error. At the bottom of the page in red, it expands on this: warning that the Java code is missing the closing (%>) tag
- It is also offering you a list of methods we can apply to a User object
If we scroll down the list we should be able to find the one we want: getGivenName()

Here the documentation is displayed too, helping us to confirm that the method getGivenName() will display the first name of the user, as opposed to their middle or last name, and it returns an object which is a String - so we can happily display this directly on our web page. Now all that time setting up the BuildingBlock Library is beginning to pay off.
65. Complete this line of code and save your changes using the saveAll button
Note that the red cross should disappear after you complete the closing Java code tag
The last step before we wrap it all up is to create our bb-manifest.xml file:
66. Right click on the folder WEB-INFO and choose New from the first menu
You will notice that there is no option for a Blackboard manifest file, or even just an XML file. Instead we will have to use a generic file type and specify the full name:
67. Click on the sub-menu item folder Empty File…
If you don’t see this, follow the instructions in the yellow box below
In the dialog that opens give the full filename:
bb-manifest.xml
and then click on the Finish button - it will open in the source editor.
File types
Since writing this page I have learnt that this sub-menu is populated dynamically. It shows you the common file types plus any others you have used recently.
As such you may well not see Empty File in your list. If this is the case don’t panic.
To create an empty file:
- Right click on the folder WEB-INFO and choose New from the first menu as before…
- Click on the sub-menu item folder File/Folder
- Scroll down to the final Category and choose Other
- Now you should be able to choose Empty File from the File Types window to the right
- Click on the Next > button
- Enter a suitable file name, in this case:
bb-manifest.xml
- Click on the Finish button
The file should now open and we are back on track ![]()
Should you return to the New menu, you will notice Empty File has made an appearance!
68. Copy and paste the following text into it:
<manifest>
<plugin>
<name value=”HelloWorld1d”/>
<handle value=”helloWorld1d”/>
<description value=”Fully-featured Hello World module”/>
<version value=”0.0.1″/>
<requires>
<bbversion value=”6.0.0″/>
</requires>
<vendor>
<id value=”lttM“/>
<name value=”Malcolm Murray“/>
<url value=”http://www.dur.ac.uk/malcolm.murray” />
<description value=”Learning Technologies Team - Durham University” />
</vendor>
<http-actions>
<config value=”"/>
<remove value=”"/>
</http-actions>
<module-defs>
<module-type ext-ref=”helloWorld1d-module” title=”Hello World Portal Module 1d” uicreatable=”false”>
<jsp-dir>module</jsp-dir>
<jsp>
<view>view.jsp</view>
</jsp>
</module-type>
<module type=”helloWorld1d-module” isadmin=”false” useraddable=”true” isdeletable=”true” isdetachable=”false” title=”Hello World 1d”>
<description>Displays the message Hello World and the user’s choice of globe </description>
<ExtraInfo />
<module-groups>
<module-group id=”Everyone” />
</module-groups>
</module>
</module-defs>
<permissions>
<permission type=”attribute” name=”user.personalinfo” actions=”get” />
</permissions>
</plugin>
</manifest>
Note that we have changed the <handle> value of this <plugin> and the <ext-ref> value used in the <module> definitions. We only need to do this if you have previous project 1 building blocks on your test server, as you can only have one building block per server with the same pair of handle and vendor values. Equally you can only have one custom module type (set by <ext-ref>) per system.
69. Replace the values coloured above in red with entries more suited to you (e.g. alter the vendor details).
70. Save your changes using the saveAll button
All being well we are ready to build our WAR file.
71. Click the buildAll button on the toolbar:

All being well after a second or so you should see the message BUILD SUCCESSFUL at the foot of the screen in the Output window:

Let’s not worry about the warning issued (in red) which notes that some of our operations are unchecked.
ANT
Did you hear the patter of tiny feet?
No, using netBeans does not guarantee that you will suddenly spawn any offspring, the tiny feet in question belong to ANT. Before you try and get me committed, I should explain. The script that “built” your project is an ANT script - a Java-based build tool - one of the packages that netBeans sets up for you when you install it. For most users, that’s all you need to know about ANT - it does what netBeans tells it to.

If you want to know more, have a look at this website:
http://ant.apache.org/
If you get errors, look in the box for pointers as to what went wrong and compare your code carefully with that here - check all tags have been closed and that you have the right number of brackets. Correct the errors and then press the buildAll button again. Repeat this process until all the errors have been fixed.
If you are having trouble completing this section, compare your efforts with this completed WAR file: HelloWorld1d_v1.war
You might also want to look at this page which helps you fix errors
NetBeans has created a WAR file for us in the dist (distribution) folder in our project:

This is the file we will upload onto our Blackboard server. The DIST folder is not visible in the Project window in netBeans (though you can see it in the Files window). On my PC it can be found here
C:\data\netBeansProjects\HelloWorld1d\dist\HelloWorld1d.war
All that’s left is to install it on your test server from the System Administrator tab:

and then add it to a page:

If you can’t remember how to do this, follow steps 15 to 26 in project 1c:
Adding Images
The examples have deviated slightly from the original “Hello World” concept. This next step helps get us back on track and modify the module to look like this:

72. Change the line of code echoed on the screen in view.jsp to read:
Hello World and Hello <%=user.getGivenName()%>
Now we are going to add an image. This is easy to do on a course tool or system control panel tool, but a bit more involved for portal modules, as you need to use a custom Blackboard class to find out where your images are stored. First things first, lets add some images…
73. Right click on the folder icon Web Pages in the Project window and choose the option New from the first menu and Folder… from the sub-menu
74. Use the dialog that opens to name the folder:
images
75. Click on the Finish button
76. Now right click on these three images (or the links) and save them on your PC in the images folder you have just created.
Europe and Africa
The Americas
Australasia
Once you have copied the file(s) across, they should appear in the images folder in netBeans.
As the images are displayed in a web browser, images used in your building blocks should be one of the standard web formats, such as GIF, JPEG or PNG
To display the globe in the module we need to add another line to view.jsp pretty much like this:
<img src=”../images/globe1.gif” alt=”the world” title=”the world” width=”52″ height=”51″>

However the web server will not understand a relative URL like “../images/globe2.gif” on a portal tab, as the URL will be interpreted as being relative to the URL of the whole tab, rather than relative to the URL of the JSP that creates the module. Thus we have to use a custom Blackboard class to help resolve this called blackboard.platform.plugin.PlugInUtil which has a static method getUri() which will return the appropriate URI (universal resource indicator).
You may have been wondering how I found out about the class blackboard.platform.plugin.PlugInUtil. There is (sadly) no mention of it in the Module Developer’s Guide. The Building Blocks Developer’s Guide contains this cryptic reference which (with hindsight) could have put me on the right track:
Obtaining a URL to the Building Block
Due to the dynamic deployment environment with Virtual Installations the Building
Block should not hard code any root-anchored, self-referencing URLs. Instead, one of
the utility methods on blackboard.platform.plugin.PlugInUtil should be used to
obtain the URL reference.
Instead, I did what most developers do when they get stuck. I posted messages to the BBDN Forum (Behind the Blackboard) and the Bb-OPEN_SRC list servers. Someone there pointed me in the right direction. If you haven’t come across these lists yet, you should investigate them.
77. Add the following import statement to the top of view.jsp:
It doesn’t matter if it goes above or below the existing one
<%@page import=”blackboard.platform.plugin.PlugInUtil”%>
78. Now move down the file view.jsp and add these lines after our changed Hello World… entry:
<br>
<img src=”" alt=”the world” title=”the world” width=”52″ height=”51″>
79. Now place the cursor between the two double quotes in the src=”" section and add a pair of Java code tags:
<% %>
80. Click the space immediately after the opening Java code tag and start typing this:
=PlugInUtil.getUri(”lttM”, “helloWorld1d”, “images/globe1.gif”)
NetBeans will try and help you complete the code:

The documentation shows that the method getUri() requires you to pass three variables (all Strings):
- vid - the vendor ID for this building block, which in my case is: lttM
- handle - the handle for this building block, which in my case is: helloWorld1d
- path - the path for the file (beginning in the Web Page folder), which in my case is happens to be an image file: images/globe1.gif
You may be wondering why we are passing these strange variables. The answer relates to the way building blocks are installed on your server. If you have a look at your Blackboard server’s files, then you might see something like this:

This picture shows the files after the helloWorld1d building block has been installed. On a Linux box, building blocks are installed in the folder:
/local/bboard/blackboard/content/vi/bb_bb60/plugins/
Each building block is installed into a subfolder of this, given a name which is created by taking the vendor id, adding a hyphen and then the building block handle. In this example you can see the installed files for project1d in lttM-helloWorld1d and above this, a folder called lttM-helloWorld created earlier (in project1a). There are no folders for project1b or project1c because I had removed these building blocks from the server before I took the screenshot! Removing a building block deletes the accompanying folder.
Now that we can add an image, we will now change the code to allow the user to pick one of three images to display. This (somewhat contrived) example shows you how to allow users to “edit” modules, and save their settings. Blackboard have provided some standard methods for doing this, which are implemented in things like the My Courses module. The Blackboard Module Developer Guide gives some examples of how to do this. We need to create a page called edit.jsp which will be displayed to the user when they click the edit button on the portal module.
First, we need to amend the bb-manifest.xml file to alert the server to the fact that the module implements the edit feature and tell it which page to display when a user clicks the button:
81. Edit bb-manifest.xml find the line:
<view>view.jsp</view>
add below it add:
<edit>edit.jsp</edit>
So that it now looks like this:

82. Now right click in the module folder and create a new JSP file called edit.
83. As before delete the standard content netBeans provides in your new file edit.jsp to leave it completely blank.
84. Just as we began with view.jsp, add the following two statements to the top of the edit.jsp so that we can make use of the Blackboard tag libraries:
<%@ taglib uri=”/bbData” prefix=”bbData” %>
<%@ taglib uri=”/bbUI” prefix=”bbUI” %>
85. Now add the opening and closing <bbData:context> tags:
<bbData:context id=”ctx”> </bbData:context>
This time we won’t use the <bbUI:docTemplate> tags. Instead we will use another pair of custom tags: <bbUI:modulePersonalizationPage> designed for use on edit pages:
86. Between the two <bbData:context> tags add:
<bbUI:modulePersonalizationPage> </bbUI:modulePersonalizationPage>
Your project should now look like this:

If we were to build the project and install the WAR file now, the module would look like this:

Note the ‘pencil icon’ which takes us to edit.jsp!
Remember to update the version number in bb-manifest.xml before you build your project if you want to over-write an existing project1d building block on your server.
We will declare a String on the page edit.jsp called chosenImage and initially set the value to be
image1.gif
The edit page will try and retrieve any previously stored value of chosenImage from the module’s CustomData. If it finds any, it over-writes the value we set as the default at the top of the page.
The rest of the page is a simple form with a select which allows the user to change the value of chosenImage. We will use some more custom tags <bbUI:step> and <bbUI:dataElement> to give this page the standard Blackboard look and feel.
If you want to see what other custom tags there are, open the file /WEB-INF/config/taglibs/bbUI.tld in netBeans
When finished the edit page should look like this:

Now let’s build it:
87. Before the opening <bbUI:modulePersonalizationPage> tag, add some Java code to declare the String chosenImage and assign it a default value:
<%
String chosenImage = “globe1.gif”;
%>

We now want to get any stored value, for which we need to use a custom Blackboard class: blackboard.portal.external.CustomData so…
88. Add an import statement at the top of the page:
<%@page import=”blackboard.portal.external.CustomData”%>
89. Now add a new line below the line that declares the String chosenImage and type :
CustomData cData = CustomData.getModulePersonalizationData(pageContext);
This creates a CustomData object called cData using information passed to it by Blackboard from the pageContext. This includes information such as which tab the module is on, what the module is called, etc. We can query cData to find out any values previously stored by the user. These values are stored as pairs of Strings on your Blackboard server - one the key (or name) and the second the stored value. We now look to see if a value of chosenImage has been set. The key name can be anything, but convention suggests that as these are user personalisations, (rather than global/admin changes) you use the format personal.keyName so that they don’t get mixed up with any other CustomData. We will look for a key called personal.chosenImage and if it is not equal to null, reassign the value of chosenImage to match:
90. Type:
if(cData.getValue(”personal.chosenImage”) != null){
chosenImage = cData.getValue(”personal.chosenImage”);
}
So that your project now looks like this:

If you haven’t saved it recently, now would be a good time to click on the saveAll button
91. It is time to add the form tags (after the opening <bbUI:modulePersonalizationPage> tag):
<form method=post action=”proc_edit.jsp”> </form>
When submitted, the user will be taken to the page proc_edit.jsp - we will create this in a minute!
92. Now we will add a Blackboard step to prompt the user for their choice of image:
<bbUI:step title=”User options” number=”1″> </bbUI:step>
As ever, if you type slowly, netBeans will help you complete the code:

This code will generate the HTML for a step like this:

Note that it honours the number and title values we specified. We will add the <select> element which will allow the user to choose a graphic to display in a minute.
93. First, let’s generate the code for a submit button. Note this uses a single tag:
<bbUI:stepSubmit title=”Submit” number=”2″/>
This creates the HTML for the final Submit (and Cancel) step:

You don’t need to specify a title for this final step - if you leave it empty the default value (”Submit”) is added automatically - but we did here, just because we could!
By now your code should look like this:

94.On the line below the opening <bbUI:step> tag add a pair of <bbUI:dataElement> tags which we use to add a field:
<bbUI:dataElement label=”View” required=”true”> </bbUI:dataElement>
This will have the label “View” and because we added the optional entry required=”true” a small red asterisk will appear next to this field showing the user that this is a required field.

Don’t be fooled by the asterisk - Blackboard doesn’t add any error checking to the field - if you want to do that, you’ll need to do this yourself using some JavaScript. We won’t bother here as the select control ensures that the user has to make a choice and limits this to three valid entries
95. In between the <bbUI:dataElement>tags add the code below to complete this element by creating a select control like this one:
<select name=”chosenImage” size=”1″>
<option value=”globe1.gif”
<% if(chosenImage.equalsIgnoreCase(”globe1.gif”)){out.print(” selected “);}%>
>Africa and Europe</option>
<option value=”globe2.gif”
<% if(chosenImage.equalsIgnoreCase(”globe2.gif”)){out.print(” selected “);}%>
>The Americas</option>
<option value=”globe3.gif”
<% if(chosenImage.equalsIgnoreCase(”globe3.gif”)){out.print(” selected “);}%>
>Australasia</option>
</select>
This is essentially a standard HTML select control with some extra Java code (shown here in red). The code sets the appropriate option entry as selected, based on the current value of chosenImage. Extra line breaks have been added here for clarity, you do not need to put the Java code on a separate line when you type this into netBeans!
96. Finally, a first time visitor may be wondering what this is all about, so add some instructions <bbUI:instructions>tags before the opening <bbUI:dataElement>tag:
<bbUI:instructions>There are three possible views of the Earth<br>
Select the one you want displayed on the module</bbUI:instructions>
Your code should now look like this:

which will create a page like this in Blackboard:

Note that because we added the <bbUI:modulePersonalizationPage> tags to this page, the breadcrumb trail has been added automatically and populated using the name of the tab (in my case Greenhouse) and the module.
Now it’s time to work on the page that is action of this form, which needs another JSP:
97. Right click in the module folder and create a new JSP file called proc_edit. Delete the standard content.
98. Add these tags as before:
<%@ taglib uri=”/bbData” prefix=”bbData” %>
<%@ taglib uri=”/bbUI” prefix=”bbUI” %>
<bbData:context id=”ctx”> </bbData:context>
This time we won’t use the <bbUI:modulePersonalizationPage> tags, as we are going to display the result of an attempt to change these values. Blackboard have another related pair of tags just for this purpose:
99. Between the two <bbData:context> tags add:
<bbUI:modulePersonalizationReceipt> </bbUI:modulePersonalizationReceipt>
Now we want to add some Java code to retrieve the existing CustomData, request the value of chosenImage supplied on the page edit.jsp, update the CustomData object and then save it. All being well we should report this success to the user.
As we will be using the class blackboard.portal.external.CustomData again, we need to:
100. Add this import statement to the top of the page:
<%@page import=”blackboard.portal.external.CustomData”%>
We will add the code to gather and set the CustomData before the <bbUI:modulePersonalizationReceipt> tag, so that we can cope with any errors rather than saying it all went well when it didn’t!
101. Before the opening <bbUI:modulePersonalizationReceipt> tag add this code (note I have added some comments which are shown here in grey):
<%
// retrieve the stored CustomData
CustomData cData = CustomData.getModulePersonalizationData(pageContext);
// request the new value from the form
String chosenImage = request.getParameter(”chosenImage”);
// set the value
cData.setValue(”personal.chosenImage”, chosenImage);
%>
We now want to save the changes.
There is a chance that an error may occur when we try and save the data back into the Blackboard database, so we will add try {} and catch{} statements around the save() command. The server will try and execute the code in brackets after the try statement. If something goes wrong an error is thrown. If the error matches the one specified in any of the following catch statements, then the server will execute the code in brackets rather than just display a standard error message. At the end of the catch statement is the command return; which tells the server to stop there and not try to process any more of the code. This stops the later <bbUI:modulePersonalizationReceipt> part from being displayed. As such this gives us more control over what the user sees, and also more clue what went wrong. The error messages used in these examples are very simple. You might want to expand them more for your purposes (e.g. indicate what action the user should take, e.g. contacting their Blackboard Administrator, or just trying again!)
More details about exception handling can be found from this website: http://www.churchillobjects.com/c/11012.html
102. Immediately below the code you just typed (but before the closing Java code tag %>) add:
// save it
try {
cData.save();
} catch (blackboard.data.ValidationException ve) {
// an error was thrown whilst creating the custom data on first access
out.println(”<p>Sorry your changes could not be saved<br >”);
out.println(”There was a problem trying to get your custom information</p>”);
return;
} catch (blackboard.persist.PersistenceException pe) {
// some other error was thrown
out.println(”<p>Sorry your changes could not be saved<br>”);
out.println(”There was a problem trying to save your custom information to the database</p>”);
return;
}
How do you know which exceptions to catch?
A good question - and netBeans comes to the rescue again. When you start to type the line cData.save(); the code completion tool gives you hints:

The box above shows which exceptions calling this method might throw. If like me, you feel that this box is rather on the small side, you can click on this button:

to see the documentation in a more readable format in your default web browser. This has the added bonus that you can then look at other methods and the detailed description of this class much more easily:

When should I use try and catch blocks?
Another good question. Some developers might argue that all the code on this page from the first declaration of CustomData in proc_edit.jsp should be placed within a try catch block, and indeed another block should have been used when we first call it on edit.jsp. In many ways they are right, but the only time it is crucial if our call fails is when we try and save the new value on this page. Thus I have only used one block. It is interesting to note that none of the examples showing you how to get and set CustomData in the Blackboard Module Developer Guide use a try catch block!
As we refered to two more Blackboard classes in our catch blocks, we need to:
103. Add these import statements to the top of the page:
<%@page import=”blackboard.data.ValidationException”%>
<%@page import=”blackboard.persist.PersistenceException”%>
Assuming no errors are thrown, the server will save the CustomData and then come across the <bbUI:modulePersonalizationReceipt> tags. It will display any text shown between them and add appropriate breadcrumb trail entries and an OK button at the foot of the page.
104. Add a suitable receipt message between the <bbUI:modulePersonalizationReceipt> tags such as:
Your chosen view has been saved.
This will be shown to the user on a page like this:

105. Save all the files using the saveAll button.
All being well your completed proc_edit.jsp file should look like this:

Now we need to update view.jsp to display the image that the user has chosen!
This is actually quite simple to do…
106. Switch to view.jsp and add this import statement below the existing two:
<%@page import=”blackboard.portal.external.CustomData”%>
107. Add a new line after the entry User user = ctx.getUser(); and add:
String chosenImage = “globe1.gif”;
CustomData cData = CustomData.getModulePersonalizationData(pageContext);
if(cData.getValue(”personal.chosenImage”) != null){
chosenImage = cData.getValue(”personal.chosenImage”);
}String imageUri = “images/” + chosenImage;
This creates a new String imageUri which points to the correct file
108. Now all that is left is to update the <img> tag and change the third variable we pass to PlugInUtil.getUri(). Instead of passing it a new String (in the past we used the “images/globe1.gif” or whichever one we wanted) now we just use imageUri. The change is highlighted below in red:
<img src=” <%=PlugInUtil.getUri(” lttM” , ” helloWorld1d” , imageUri) %>” alt=” the world” title=” the world” width=” 52″ height=” 51″ >
109. Update the version number in your bb-manifest.xml file. Save everything and build your project. If you have no compilation errors, you should have the final WAR file which you can now upload onto your server. It should remember you choice of globe ![]()

If you are having trouble compiling your final module, compare your efforts with this completed WAR file: HelloWorld1d_v2.war
You might also want to look at this page which helps you fix errors
110. Put the kettle on. Treat yourself to some chocolate: Step 1d is complete.
This example is lengthy because we spent a lot of time downloading and configuring software. We don’t need to do that again, so future efforts should be much quicker to yield results. In the next project we will change this code to support internationalisation. Instead of hard-coding the text to display, we will add a variable and set it at run time according to the appropriate Locale. This is really the icing on the cake.
If you have any comments to make about this example, please let me know using the link below:
Comment by Julie
Took some time but apart from the odd mistype it worked great - looking forward to 1e!
Posted on February 14, 2006 at 12:54 pm
Comment by Cathy
This is great and pitched at just my level!
One small suggestion. It would be helpful if the hyperlinks to Bb and Sun could launch a new browser window (target=”blank”).
Posted on March 3, 2006 at 9:07 am
Comment by Malcolm
Hi Cathy,
Glad you like it. You comment about the external links is a good point. They should all now be set with a target="_blank" as you suggested.
Thanks
Posted on March 3, 2006 at 2:51 pm