Usability - Productivity - Business - The web - Singapore & Twins

By Date: July 2010

Command line tool for Linux command line junkies

I found a little gem for "Friends of the commandline" TM . Since the autor suggested to improve this with zenity I went ahead and created ~/bin/runthis:
# Runs the command on the command line or prompts that have been entered in the prompt box
# Originally published here: http://crunchbanglinux.org/forums/topic/2848/bash-at-your-fingertips-nifty-notifysend/
# Replaced command line with zenity dialog
if [ -z "$1" ]; then
    what=$ (zenity   --entry   --text "Type a command" );
    what= $1
info=$ ( eval "$what"   2 > /dev /null );
notify-send -t $ ( ( 1000+ 300 *` echo -n $info | wc -w ` ) ) -u low -i gtk-dialog-info "$what" "$info" || exit 2
Make it executable with chmod +r ~/bin/runthis. Final step: go to the CompitzConfig Settings Manager (or the Gnome shortcut settings) and assign it a key combination (I use Ctrl+Alt+r). Now you can hit the combination, enter a command and the result will be shown in a notification dialog. For the rationale, read the original source
As usual YMMV

Posted by on 19 July 2010 | Comments (0) | categories: Linux

Run <strike>SUN</strike>ORACLE VirtualBox with KVM modules on

KVM and VirtualBox are two virtualization solutions for Linux. KVM requires the module kvm_intel to be loaded, while VirtualBox can't run with the module running. So if you can't makeup your mind what to use, you need a little script magic to get this working. Here are the steps:
  1. Download libnotify for nice bubble notifications (just eye candy):
    sudo apt-get install libnotify (Of course you can use the Synaptics package manager in System-Administration and simply search for the library, but that's not sporty)
  2. Create a script (see below) in ~/bin/vboxkvm
  3. Make it executable with chmod +x ~/bin/vboxkvm
  4. Change the command in the VirtualBox menu entry from VirtualBox to vboxkvm
The script (Keep in mind that Linux is cAsEsEnsItIvE):
SERVICE= 'kvm'
if ps ax | grep -v grep | grep $SERVICE > /dev /null
    notify-send -t 10000 -u low -i gtk-dialog-info "Removing kvm and starting Virtualbox"
   gksu "modprobe -r kvm_intel"
    notify-send -t 10000 -u low -i gtk-dialog-info "OK kvm not loaded,  Virtualbox starting"
VirtualBox $
As usual: YMMV

Posted by on 19 July 2010 | Comments (0) | categories: Linux

Getting close with IBM OneUI

I recommended the use of IBM OneUI before including a helper stylesheet. While there is a complete 2.0 documentation (as well as the 2.1 version or v3.0 version) available, a little explanation about the how and why goes a long way. The OneUI is not so much about colours and fonts, but about structure, navigation and layout. Following its guidelines your applications get a consistent look, which user studies show increase user confidence about their ability to use your web application. This translates into less help desk calls. Any consistent framework does that, the advantage of the OneUI is that your applications start looking consistent to the IBM software in use. Furthermore when IBM releases a new visual style, your application can adopt it in no time. Lets have a look at the layout of a OneUI page. It consists of the following structure:
  • Global Navigation: Branding (corporate and application logo), links to core applications, usern ame, login and help
  • Application Navigation: Typically a tab bar on the left and the search box on the right. Search, while potentially acting on the current page content is considered an application level function. You could use a dropdown to allow the user to define the search context
  • Place / Page Navigation: Contains the page title on the left and a series of page level actions on the right. A page would be the place where you conclude an entire interaction. This is different from the Notes client or classic web application model where view (selection of information to act on) and document (the instance of information) are usually on different pages. Some rethinking required (Partial page refresh is your friend!)
  • Main Content: The main content area has one to three columns where the left and right column are optional. If the main content needs navigation or filtering that would be displayed in the left column with navigation above filters. In the right column contextual information can be displayed (e.g. in a Claim form the right column would show the account summary)
  • Footer: The footer area contains logo, copyright and related links that are not frequently uses (e.g. Feedback, documentation, other software)
Drawn on a wire frame you get this:

An empty page just containing the layout would look like this:
OneUI in green
This is also available in blue, red, gold, metal, black and white and it even is usable using Lynx. Branding fanatics will highlight that the logo area is tiny and the footer to long, however nobody stops you from altering the CSS, as long as you stick to the structure (the small logo saves valuable space above the fold). Some Lynx impressions:

Read more

Posted by on 17 July 2010 | Comments (6) | categories: XPages

Multi column checkboxes and radio buttons

Brian shared his implementation of multi-column check boxes. Having such a control mimicking the functionality of the Notes client is pretty helpful. Brian's approach however wasn't very re-usable, so I though of a way to come up with a more reusable solution:
Multi column check boxes in various views
The resulting custom controls can be easily added to any page with a few lines of code. You need to reference 2 supporting JavaScript libraries and then add a few lines to your page:
  1. < xc:MultiColumnCheckBox numberOfColumns="3"dataBindingName="document1.CheckDemo">
  2.     < xc:this.FieldValues> < ![CDATA[#{javascript:return ["black","blue","red","yellow","white","brown","gray","vomit","green"];}> </xc:this.FieldValues>
  3.     < xc:this.disabledValues> < !CDATA[#{javascript:return ["gray","vomit"}]]> </xc:this.disabledValues>
  4. </xc:MultiColumnCheckBox>
You have 4 parameters:
  • numberOfColumns: (Integer) number of columns to use
  • dataBindingName : (String) field to bind the control to including the datasource
  • FieldValues: (Array of Strings) Values to offer in the control
  • disabledValues: (Array of Strings - optional) Values that are disabled
The same parameters also work for a group of radio buttons:
  1. < xc:MultiColumnRadioButton numberOfColumns="3" dataBindingName="document1.RadioDemo">
  2.     < xc:this.FieldValues> < ![CDATA[#{javascript:return ["Porsche", "VW", "BMW", "Mercedes", "Opel", "Ford", "Audi","Lada"]}]]> </xc:this.FieldValues>
  3.     < xc:this.disabledValues> < ![CDATA[#{javascript:return ["Lada"]}]]> </xc:this.disabledValues>
  4. </xc:MultiColumnRadioButton>
The controls are available for download on OpenNTF (of course you use Guo Yi's Import and Export for Designer for this). Enjoy and share your ideas for v1.1
As usual YMMV.

Posted by on 13 July 2010 | Comments (e) | categories: XPages

Is Internet Explorer holding you back?

There is a paradoxon going on in corporate IT (probably more than one): On one hand developers whisper "Our standard is IE[6]" on the other hand managers buy iPhones, iPads, Android phones (which are all WebKit based) and demand that all applications should move to browser based access, being Intranet or the cloud. Development for browsers is painful compared to client environments (you need to know at least 4 totally unrelated - in terms of syntax - technologies: HTML, CSS, JavaScript, HTTP). HTML5 will address some of the pain (while you still have to learn the 4 technologies). Looking at the browser's HTML5 capabilites you only can conclude, that your mobile device will outshine your desktop browser by a large margin:

Summary of HTML5 support per browser

Calculation of support of currently displayed feature lists

Internet Explorer Firefox Safari Chrome Opera
Two versions back 6.0: 3% 3.0: 42% 3.2: 57% 3.0: 76% 10.1: 51%
Previous version 7.0: 10% 3.5: 70% 4.0: 78% 4.0: 81% 10.5: 71%
Current 8.0: 25% 3.6: 76% 5.0: 86% 5.0: 85% 10.6: 77%
Near Future (2010) 8.0: 25% 4.0: 90% 5.0: 86% 6.0: 89% 10.6: 77%
Future (2011 or later) 9.0: 58% 4.0: 90% 5.*: 88% 7.0: 90% 10.7: 78%
( Table found here). IBM made the decision to move to Firefox. Is it time for you to move too? A few places to check out on the new capabilities:

Posted by on 13 July 2010 | Comments (3) | categories: Software

eLearning day at Catholic High School

Yesterday some schools in Singapore conducted an eLearning day including Catholic High School where my boys are attending P4. The eLearning day's purpose was to gather experience how school can continue if access to a school campus isn't feasible ( natural disaster or an epidemic like SARS or Swine flu). The boys suspected rather different motives for the date set:" They just don't want us to see tired teachers since they were up all night watching the world cup finals" (In German we would say to such a statement: Kindermund tut Wahrheit kund <g>). Working from home I rather curiously watched how that eLearning would work. It became very obvious that this day was very experimental. On one side that is good. Only through experimentation experience can be build to find the right mix for optimum performance. On the other side the way the tools were used was utterly inadequate for a comprehensive eLearning experience. All teachers by now should have access to a gMail account and Google docs. The students access their learning materials through the Lead portal, which is also linked to Google docs and their schools website. I made the following observations:
  • The website didn't cope well with the load and some alternative access had to be given. Having not prepared for the estimated load is bad, but remedial actions like notifying via SMS for the alternate access worked well
  • Tasks arrived in many ways: some were posted on blogs, some on the school website, some were emailed. All in all it felt quite messy. I would expect an integrated homework assignment system that works for students every day
  • Submission of completed work items, unsurprisingly was messy too. Some teachers required the documents to be emailed back, some wanted a form based on Google spreadsheets to be filled
  • There was a total format mess. Everything could be found: PDF, Doc, PNG. Very messy to process, especially when you are on a Mac without MS Office. I taught Ernest how to download files and then re-import them into Google docs
  • There was no real eLearning at all. All the tasks were file representations from worksheets that used to be paper handouts in class. Big disappointment there
  • None of the tasks were collaborative. Everything was solitary: complete your assignment and be done with
  • The sharing facility of Google docs wasn't used. Instead eMails were used to share documents. Did MOE miss something when opting for Google docs? Training eventually?
  • Teachers were not available for chat to ask questions or discuss results. I would have expected at least Google chat or an active bulletin board. While we (I live here 10 years, so I think it is OK for me to speak of "people of Singapore" as we) pride ourselfs with a world class network infrastructure I can understand that in a first of such events video and voice chat was out. In contrast on a recent team assignment Ernest and his school buddy happily chatted away on Skype video chat (but 1:1 video is much easier than catering to a whole class and many of them concurrently)
  • The LEAD portal has a Windows dependency for a series of functions. So no iPad for the gentlemen anytime soon
I'm sure these topics will be brought up in a de-briefing (at least I hope so). So what would I change?
  • Improved Homework and task assignment and submission. Preparing for a digital world the schools should adopt such a system for general use, not just for the eLearning day. It also would ease the teachers' task of tracking who has submitted an assigned task and who has not. A good starting point could be the applications listed at schoolforge.net, OS4ED, OSEF, EduTech (University of Geneva), EduCause or Wikipedia (also here)
  • More collaborative task to learn teamwork. E.g. a task could be split into 2 where one part is to obtain information from a peer and interpret/analyse it
  • Back channel: eLearning without chat/chat room is incomplete
  • Full use of Google documents (I like LotusLive's collaborative editor better, but that's not what the schools bought) instead of emailing stuff around. No more use of other formats. This also would include reformatting work for use as electronic documents rather than files that only look good when printed
  • Material that is better suited for eLearning and properly managed. Maybe Moodle would be a good start. Of course creating good eLearning materials is hard and creating dual use material even more.
  • Mobility enabled content: works on Android, i[Touch|Phone|Pad|Mac], Linux tablets

Posted by on 12 July 2010 | Comments (2) | categories: Singapore

Leaders of the fee world

Basic salary is a very crude measure to gauge total perks for elected politicians, but its a number easy to compare. Especially compare to the countries GDP per person. India's Manmohan Singh's base salary is pretty much in line with India's GDP per person and the lowest in a recent comparison by the Economist. On the other end are South Africa, Indonesia and Singapore (and Kenia, if the salary increase would have been accepted). Impressive figures:

What would be interesting is to see a table with 3 columns: Base salary, total perks (including seats on boards etc.), personal wealth. And of course a small country need to pay their leaders well. After retiring from a political job, there isn't that much else to do. Just ask MM why he sticks around.

Posted by on 12 July 2010 | Comments (1) | categories: Singapore

Meet the XSP object

Look at the source code of any XPage in your browser (you are are using Firebug, are you? *) and you will find numerous invocations to the XSP object. This object sits on top of Dojo (at least for now) and is one of the reasons why type-ahead and partial refresh work so easily in XPages. Having a closer look at the object reveals a wealth of possibilities. A good starting point is an article by Teresa Monahan (one of our code goddesses in the IBM Dublin Software lab) on the Domino Designer WIKI. To get a deeper look simply examine the source. You can find the file in [Your Notes/Domino data directory]/domino/js/[DojoVersion]/ibm/xsp/widget/layout/XSPClientDojo.js.uncompressed.js. Let us have a look at some of the interesting functions and possibilities. XSP has a strong heritage in Dojo, but IBM might provide alternate implementations for specific needs in future. To get a head start in Dojo check out Mastering Dojo , Dojo: The Definitive Guide , Practical Dojo Projects or the other Dojo books . But back to XSP:
  • XSP.addOnLoad( listener )
    Adds a function "listener" to the code that is executed when loading a page.
  • XSP.attachClientScript(targetClientId, _event, clientScript), xsp.attachClientFunction(targetClientId, _event, clientSideScriptName)
    Attaches a function to a client element for a specific event. If the function exists use attachClientFunction. If the code is inside the variable "clientScript" use attachClientScript
  • XSP.setSubmitValue(value)
    Overwrites what a submission is sending back to the server. Use with caution!
  • XSP.canSubmit()
    Returns true/false. If a page has been recently submitted it returns false. Good way to prevent submit button double click hazards. To manually set it to true call XSP.allowSubmit()
  • XSP.addPreSubmitListener(formid, listener, clientId, scriptid)
    Adds a function (the listener) that runs before the form (formid) is submitted. Great way to run cleanup code in custom controls to make them self contained. Also a good way to have code run only for a specific button (clientid) rather than onsubmit in any case. The scriptid helps to check if the listener has been loaded already (e.g. on partial page refresh) to avoid duplicate loads. If listener returns false submission does not happen. Runs after the client side validators.
  • XSP.addQuerySubmitListener(formId, listener, clientId, scriptId)
    Same as above but runs before the validators
  • XSP.alert(message), XSP.error(message), XSP.confirm(question)
    Currently this are wrappers around alert() and dojo.confirm(). Using this functions has the advantage of instant beautifications once the XSP object gets upgraded
  • XSP.attachValidator(clientId, validator, converter)
    Allows to attach a validator to a client object. The better way to validate than writing code in the onSubmit event. Admittingly a little more complex. The function warrants its own blog entry (stay tuned)
  • XSP.validateAllFields(true|false)
    Default for this is false. When set to true all client side validation is executed not only all until the first error. You will get a number of prompts. Makes sense with an overwritten validation function, but that's a story for another time.
  • XSP.attachEvent(clientId, targetClientId, _event, clientSideScriptName, submit, valmode, execId)
    Attaches events to client side elements. The clientId is the element, the targetClientId the target for a partial refresh, _event the event to attach. More in a future blog entry.
  • XSP.fireEvent(evt, clientId, targetId, clientSideScriptName, submit, valmode, execId)
    Allows to trigger an event script.
  • XSP.attachPartial(clientId, targetId, execId, eventName, scriptName, valmode, refreshId, onStart, onComplete, onError)
    Attaches events to client side elements. The clientId is the element, the targetId the target for a partial refresh, eventName the event to attach. on(Start|Complete|Error) the functions to run locally around the ajax event. More in a future blog entry.
  • XSP.partialRefreshGet(refreshId, options), XSP.partialRefreshPost(refreshId, options)
    Triggers a partial refresh manually. For POST the refreshId must point to a form, for get it can be any element with a partial refresh defines. The options object is quite interesting to examine. Includes parameters and functions to execute in the life cycle.
  • XSP.attachDirtyListener(clientId)
    Listen to a specific field. If it changes consider the form "dirty". (Persil to the rescue). Use XSP.attachDirtyUnloadListener(message) to set a message. Typically you use that for "Your form has changed, do you really want to navigate away" type of messages.
  • XSP.trim(string), XSP.startsWith(string, seachString), XSP.endsWith(string, searchString)
    Convenience functions for string stuff.
  • XSP.serialize(object)
    Takes an JavaScript object and returns a textual representation. Currently JSON, but might change.
  • XSP.toJson(object), XSP.fromJson(string)
    Convert data from/to JSON format.
  • XSP.log(message)
    Send a message to the local log. Used for debug (mostly)
This is just an extract. I probably will cover usage details in future posts. You will have fun exploring more of it. As usual: YMMV

*Remark: Regardless what target environment environment you develop for: IE6 , IE7, IE8, IE9, FF2 , FF3, FF4, Opera, WebKit (The core of the browsers in iPhone, iTouch, iPad, Android, Chrome and Safari) or others, you do want to develop your XPages application using Firefox. The simple reasons are Firebug and Medusa.

Posted by on 11 July 2010 | Comments (2) | categories: XPages

Taming the categorized views - reloaded

My previous solution was designed for categorised views that don't use column totals. The difference between a view control with and without column totals is subtle but important. Without totals a category row is rendered with a colspan attribute that allows the categories to freely flow over the whole width of the table. Are column totals present no colspan is used and the column for the category must be wide enough to accommodate the full content. With a little JavaScript that can be fixed. The server side JS now looks like this:
  1. function getClientJSforTableLayout (tableID ) {
  2.     /**
  3.       * This code has as result JavaScript source code that is send down to the client]
  4.     */
  5.     var curTable = getComponent (tableID ) ;
  6.     var curID = getClientId (tableID ) ;
  7.     var tablewidth = extractWidthFromStyle (curTable. viewStyle ) ;
  8.     var result = "fixCatTableSpan(\"" +curID ;
  9.     result += "\");\n" ;
  10.     result += "fixCatTableWidth(\"" ; // This holds the JavaScript we return to the browser
  11.     result += curID + "\",[\"" ;
  12.     // Here we compute the sizeString
  14.     var kids = curTable. getChildren ( ) ;
  15.     var defaultPercent = Math. floor ( 100 /kids. size ( ) ) + "% " ; // You have to love flexi data types
  16.     var perCentArray = [ ] ;
  17.     for ( var x in kids ) {
  18.         var styleCandidate = x. style ;
  19.         if (styleCandidate == null ) {
  20.             perCentArray [perCentArray. length ] = defaultPercent ;
  21.         } else {
  22.             var candidate2 = extractWidthFromStyle (styleCandidate ) ;
  23.             if (candidate2 == "" ) {
  24.                 perCentArray [perCentArray. length ] = defaultPercent ;
  25.             } else {
  26.                 perCentArray [perCentArray. length ] = candidate2 ;
  27.             }
  28.         }  
  30.     }
  31.     result += perCentArray. join ( "\", \"" ) ;
  32.     result += "\"],\"" +tablewidth + "\");" ;
  33.     return result ;
  34. }
  36. function extractWidthFromStyle (styleCandidate ) {
  37.     var result = "" ;
  38.     var whereisWidth = styleCandidate. indexOf ( "width:" ) ;
  39.     if (whereisWidth < 0 ) {
  40.         result = "" ;
  41.     } else {
  42.         var workString = styleCandidate. substr (whereisWidth + 6 ) ;
  43.         var hasSemiColon = workString. indexOf ( ";" ) ;
  44.             if (hasSemiColon < 0 ) {
  45.                 result = workString ;
  46.             } else {
  47.                 result = workString. substr ( 0 ,hasSemiColon ) ;
  48.             }
  49.     }
  50.     return result ;         
  51. }

Read more

Posted by on 09 July 2010 | Comments (3) | categories: XPages

Developing your XPages skills

When a body of knowledge reaches a certain size specialisation kicks in. So while we all can try to be like Leibniz or Da Vinci at the end some specialisation keeps us sane. With the growing body of knowledge in XPages you need to think where you want to go deep unless of course you are a Jack of all trades
XPages skills go in all directions
There are different directions to go: front-end or back-end, business applications or common components.
  • When you look at the back-end you want to understand the Domino server and storage in detail. You will encounter XPages' JSF underpinning and manage quite some beans. You will boldly go where "no LotusScript programmer has gone before". That is an area where knowledge of Java is a must. You might link XPages to host systems or RDBMS taking advantage of all the Java libraries at your hand
  • Deep inside the back-end there is the OSGI extension model. You write your own components to extend the available capabilities. Most likely you don't write them for a single purpose but for general reuse
  • On the front-end you want to understand JavaScript and the Dojo Toolkit. When you build shared components and controls you design them to be parameterised for easy reuse. The Dojo types you create can simply be used by your business developers in a declarative way. You have a good understanding of the IBM OneUI and reasonable CSS skills
  • You are the UI guy. You know CSS inside out, as well as all the Browser quirks. You design new Themes
All these specialisation types were almost non-existent in classic Notes development and you actually can get away without them still building great Domino applications on XPages. You are the Lotus Domino Business application developer (a.k.a the Classic): You work with what is given. Only now the group of givers has grown (see above). You work with script and are OK to switch from LotusScript to JavaScript. The only thing you care about Dojo is to know the name of the controls to put in the XPages control properties. You are perfectly happy using the OneUI. AND: You understand your users (you always have), prototype applications rapidly and create happy users before morning tea break. That was what Notes and Domino development are about: rapid user satisfaction.

Posted by on 09 July 2010 | Comments (3) | categories: XPages

I did *NOT* create the AppStore wars video

Major blunder in This week in Lotus Episode 6. I have not been asked by Stuart McIntyre or Darren Duke regarding authorship of the AppStore war video. Nevertheless they seem to know that I created it. I did not. It is not my style of language and attributing me as the author will lead to serious trouble for me (there is something like IBM employee conduct guidelines that exclude this type of language for IBMers). I discovered it on YouTube (after being tipped off). Guys you owe me an apology.

Posted by on 05 July 2010 | Comments (3) | categories: IBM Notes Lotus Notes

The Pursuit of Happiness

Happy independence day to everybody in the US. The Declaration of Independence is a remarkable document. Its preample sums it up: " We hold these Truths to be self-evident, that all Men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty, and the pursuit of Happiness ". Read it twice: " pursuit of Happiness". A common critique of today's United States is that "happiness" gets confused with "property". Common wisdom says " You can't buy happiness" (while Garfield thinks you rent it). But what is happiness? Since it is mentioned as a fundamental right equal to Life and Liberty it is worth to have a look. Luckily there are sufficient words to explain the different aspects (while in German we only have one word " Glück"). Lets have a look and start with Søren Kierkegaard who is attributed with " The unhappy person is never present to themself because they always live in the past or the future". This is in line with The Buddha's teachings: " Here and Now" or the advice from the peaceful warrior: " Whenever attention wanders to regrets or anxieties, come back to the present with this reminder: Here and now, breathe and relax.". Being in the here and now you can recognise five types of happiness (in no particular order):
  • Community: Love, relationships, being with family, friends, kids and loved ones. Appreciate, foster, procreate
  • Serendipity: Opportunities, funny encounters, the lottery ticket that wins, the coin on the street. See, encounter, enjoy
  • Pleasure: The piece of chocolate, the new car, the cool beer in the hot afternoon (small doses work better than more of it). Take your time, sense, indulge
  • Flow: Challenge yourself, leave your comfort zone. Flow, grow
  • Bliss: Beauty of nature, the unexpected smile, connected to the creator. Endure the silence, experience nature, be - don't act (the Chinese would say 无为 - Wu Wei)
(adopted from Glück kommt selten allein .. by Eckart von Hirschhausen).
Serendipity is the most unreliable, pleasure the easiest to achieve and the first to turn shallow, flow the gate to bliss and community the foundation and goal. The Buddha for a reason offers refuge to " Buddha, Dharma, Sangha" (The Enlightened One, the teachings and the community).

Happy independence day!

Posted by on 04 July 2010 | Comments (2) | categories: After hours

Accessing "Arbitrary Data" in Notes Documents (Sametime BuddyList) followup

Yesterday I stated " Neither the LotusScript nor Java API allows us to process this item type" about a Notes item type "Arbitrary Data". Today I stand corrected. It turns out, than since R65 we have NoesDocument.GetItemValueCustomDataBytes and NotesItem.GetValueCustomDataBytes. Carl Tyler from Epilio (Remember: Sametime without Epilio is like Sushi without Wasabi) filled in the missing blanks. The method requires a data type and Carl shared that the data type for the BuddyList is UbqOpaque. The second important information: Buddy lists are stored in an item named "8193". If a buddy lists grows to big additional items are added "8193.1" "8193.2" etc.
So I wrote a little agent that now extracts the whole buddylists into the C:\export\ directory. One interesting observation. All budy lists (and I had some with double byte names) started with the bytes 110 7 0 0 before the <?xml... First I though that to be Unicode Byte Order Marker (BOM), but it seems they are not related to that. So when you want to process these files you might need to edit them first. Inside my Java class I take care of that. The updated code can be downloaded including the source code as before. When running the report I found that I had to open and save the exportbuddies.xml before the XSLT transformation would run properly.
As usual: YMMV

Read more

Posted by on 01 July 2010 | Comments (2) | categories: Show-N-Tell Thursday