Tuesday, May 18, 2010

DateConverter tip - get weeknumber in one line

If you want to get the week number from a date:
DateConverter.dateToString( date, 'w' )
The DateConverter script

Friday, May 7, 2010

XPages: Severe bug in the partial event hijacker

Update 11.10.2010 Found another bug. onComplete can sometimes be a string. Fixed.

For the life of me, I couldn't understand why the onStart/onError/onComplete events didn't fire for partial refreshes. Today it suddenly dawned on me. It was because of my partial event hijacker.

The previous version overwrote the onStart/onError/onComplete events. Below is the fixed code. Sorry if you've used the hijacker and suffered the same fate as me.

function hijackAndPublishPartialRefresh(){
// Hijack the partial refresh
XSP._inheritedPartialRefresh = XSP._partialRefresh;
XSP._partialRefresh = function( method, form, refreshId, options ){
// Publish init
dojo.publish( 'partialrefresh-init', [ method, form, refreshId, options ]);
this._inheritedPartialRefresh( method, form, refreshId, options );
}

// Publish start, complete and error states
dojo.subscribe( 'partialrefresh-init', function( method, form, refreshId, options ){
if( options ){ // Store original event handlers
var eventOnStart = options.onStart;
var eventOnComplete = options.onComplete;
var eventOnError = options.onError;
}

options = options || {};
options.onStart = function(){
dojo.publish( 'partialrefresh-start', [ method, form, refreshId, options ]);
if( eventOnStart ){
if( typeof eventOnStart === 'string' ){
eval( eventOnStart );
} else {
eventOnStart();
}
}
};

options.onComplete = function(){
dojo.publish( 'partialrefresh-complete', [ method, form, refreshId, options ]);
if( eventOnComplete ){
if( typeof eventOnComplete === 'string' ){
eval( eventOnComplete );
} else {
eventOnComplete();
}
}
};

options.onError = function(){
dojo.publish( 'partialrefresh-error', [ method, form, refreshId, options ]);
if( eventOnError ){
if( typeof eventOnError === 'string' ){
eval( eventOnError );
} else {
eventOnError();
}
}
};
});
}

Thursday, May 6, 2010

XPages: Simple string encrypter/decrypter

I needed a simple encryption routine today. I found this article with accompanying code.

Here's the routine ported to Server Side JavaScript:
function StringEncrypter( encryptionScheme:String, encryptionKey:String ){
try{
encryptionScheme = encryptionScheme || this.DESEDE_ENCRYPTION_SCHEME;
encryptionKey = encryptionKey || this.DEFAULT_ENCRYPTION_KEY;
if( encryptionKey.length < 24 ){
throw new java.lang.Exception(
'Encryption key was less than 24 characters.' );
}
var keyAsBytes = encryptionKey.getBytes( this.UNICODE_FORMAT );

switch( encryptionScheme ){
case this.DESEDE_ENCRYPTION_SCHEME:
this.keySpec = new javax.crypto.spec.DESedeKeySpec( keyAsBytes ); break;
case this.DES_ENCRYPTION_SCHEME:
this.keySpec = new javax.crypto.spec.DESKeySpec( keyAsBytes ); break;
default:
throw new java.lang.Exception(
"Encryption scheme not supported: " + encryptionScheme );
}

this.keyFactory = javax.crypto.SecretKeyFactory.getInstance( encryptionScheme );
this.cipher = javax.crypto.Cipher.getInstance( encryptionScheme );
} catch( e ){ /* your exception handling */ }
}

StringEncrypter.prototype = {
DESEDE_ENCRYPTION_SCHEME: "DESede",
DES_ENCRYPTION_SCHEME: "DES",
DEFAULT_ENCRYPTION_KEY:
"The ships hung in the sky in much the same way that bricks don't.",
UNICODE_FORMAT: "UTF8",

keySpec: null,
keyFactory: null,
cipher: null,

encrypt: function( unencryptedString ){
if( !unencryptedString ){ return ''; }

try {
var key = this.keyFactory.generateSecret( this.keySpec );
this.cipher.init( javax.crypto.Cipher.ENCRYPT_MODE, key );
var clearText = unencryptedString.getBytes( this.UNICODE_FORMAT );
var cipherText = this.cipher.doFinal( clearText );

var base64encoder = new sun.misc.BASE64Encoder();
return base64encoder.encode( cipherText );
} catch( e ){ /* your exception handling */ }
},
decrypt: function( encryptedString ){
if( !encryptedString ){ return ''; }

try {
var key = this.keyFactory.generateSecret( this.keySpec );
this.cipher.init( javax.crypto.Cipher.DECRYPT_MODE, key );
var base64decoder = new sun.misc.BASE64Decoder();
var clearText = base64decoder.decodeBuffer( encryptedString );
var cipherText = this.cipher.doFinal( clearText );

return new java.lang.String( cipherText );
} catch( e ){ /* your exception handling */ }
}
}
Example usage:

Encryption
var encrypter = new StringEncrypter();
encrypter.encrypt( unencryptedString );
Decryption
var encrypter = new StringEncrypter();
encrypter.decrypt( encryptedString );
As you can see from the constructor, you can specify your own encryption key/choose from two encryption methods.

I simply ported the code, so if you want more details, read the article from which I snagged the code.

Monday, May 3, 2010

Using the Rhino JavaScript engine in XPages

Not sure how useful this is yet, but if you want to use the Rhino engine with XPages, here's how:
var rhinoEngine = new javax.script.ScriptEngineManager().getEngineByName(
"JavaScript");
// to eval JavaScript string: rhinoEngine.eval( jsString );

Oracle Tutorial

General API for javax.script.ScriptEngine

Java 6 uses Rhino v1.6R2 (documentation/etc)


One thing that just came to mind is maybe to use Rhino where IBM JScript fails.

E.g.
String.prototype.rhinoMatch = function( regExpString, modifiers ){
var rhino = new javax.script.ScriptEngineManager().getEngineByName("JavaScript");
rhino.eval(
'var result = "' + this + '".match( ' + regExpString + modifiers + ' )' );
return rhino.get( 'result' )
}

'{lastName}, {firstName}'.rhinoMatch( '/\\{(\\w+)\\}/', 'g' );
// returns array, ["{lastName}", "{firstName}"]

Saturday, May 1, 2010

XPages: Dynamically binding and unbinding server side events

Back in my early XPages career, I asked the readers if it was possible to Dynamically bind server side events.

The only answer I got was this:
The short answer: NO.
The long: it is somewhere on the list.


I don't like those kinds of answers.

After todays experiment, I can firmly say: Yes, it's possible!
This is what you'll be able to do with the simple framework provided in the demo:

  • Dynamically bind/unbind custom events

  • Bind multiple event handlers to one event

  • Run the event in a specified scope

  • Use regular HTML elements as event triggers


This started out as an experiment into Server Side Event Delegation. A couple of nights later, I further improved on the functionality.

In the slumbering stage this morning, I discovered that I was on the path for something, but not what I initially thought I had started out for. The two first experiments lead to what would have been a complex unusable blob of functionality.

Today, I did some cleanup. I removed all the class attributes, and rewrote the framework code to be more flexible. The previous two version relied on an eventHandler node in the top of the XPage, which results in a generated script tag. This implementation doesn't add any script tags to the generated html.

The server side part of the code lets you specify what to do for certain events. The client side part of the code specifies what server side event to run, what data to send to the event handler (optional), and which part of the page should be updated after the event has run.

I did a little screen capture of the demoapp in action (not visible in Google Reader):





Simple Server Side Event Delegation:
The two first buttons toggles (when active) visibility of a couple of computed fields. The third button binds/unbinds the event handler for the two preceding buttons.

Multiple Event Handlers:
The first button triggers a custom event on the server. The second button adds an event handler to the event. As you can see from the demo, for each event handler added, the addition increases by 1.

Event handler running in a specified scope:
The event handler for the event runs in the scope of an object on the server. Then the event is triggered, a viewScope variable is created from a property fetched from the object ( viewScope.put( 'theAnswer', this.theAnswer ) )

In View Edit:
When the name of the person is clicked, an "edit" event is triggered. When the ok button is clicked, a "save" event is triggered.

If you add the event handlers afterRestoreView or before-/afterRendreResponse, an event handler is added for every partial refresh. Adding them in an event handler or in the afterPageLoad event should be safe.

>> Download Demoapp

If you don't like a lot of generated server side script tags for events/embedding the event code in control after control, this is definitely worth looking at.

If you find any bugs/want more info about something, leave me a comment.

Share and enjoy!