Thursday, June 24, 2010

XPages: Simple function to clear scoped variables

All scope objects (applicationScope, sessionScope, etc.) are maps, so it's quite easy to clear them. This might come in handy during development, if you want to clear the applicationScope.

I've tested it on both applicationScope and sessionScope, and it doesn't seem to do any harm. After the maps are cleared, the server automatically loads the "internal" values used by the system.

function clearMap( map:Map ){
// Get iterator for the keys
var iterator = map.keySet().iterator();

// Remove all items
while( iterator.hasNext() ){
map.remove( iterator.next() );
}
}
Usage:
clearMap( applicationScope )

Share and enjoy!

Tuesday, June 22, 2010

XPages: Adding default value for empty view columns

Go to source mode, and find the column you want to add the default text. Add a computed field that's rendered when:
!( this.getParent().getValue() )

E.g.
<xp:viewColumn columnName="notes" id="colNotes">
<xp:viewColumnHeader value="Notes"></xp:viewColumnHeader>
<xp:text value="No notes"
rendered="#{javascript:return !( this.getParent().getValue() );}" />
</xp:viewColumn>

this.getParent() returns the cell. If the cell has no value, the computed field is visible.

You can achieve the same in the Notes column for the view (@If...), but I don't like mixing data and presentation.

Thursday, June 17, 2010

XPages: Code snippet for Multi Value Custom Converter

I got a question regarding how to work around the buggy multi-value implementation in XPages. I use a custom converter for my multi value fields.

var Converters = {  
multivalue: {
// separator: String or RegExp
getAsObject: function( valuesString, separator ){
try {
separator = separator || ',';
var values = valuesString.split( separator );

// Trims empty values
var trimmedValue, trimmedValues = [];
for( var i = 0; i < values.length; i++ ){
trimmedValue = values[i];
// Removes leading and trailing white-space
if( trimmedValue ){
trimmedValues.push( trimmedValue.replace( /^\s+|\s+$/g, '' ) );
}
}
return trimmedValues;
} catch( e ){ /* Exception handling */ }
},
getAsString: function( values, separator ){
try {
if( values.constructor !== Array ){ values = [ values ]; }
separator = separator || '\n';

return values.join( separator );
} catch( e ){ /* Exception handling */ }
}
}
}
Put the above code snippet inside a script library. Add a custom converter to the multi value field.

In getAsObject -> Converters.multivalue.getAsObject( value, separator );
In getAsString -> Converters.multivalue.getAsString( value, separator );

getAsObject is the conversion of the submitted value to the stored value.
getAsString is the conversion of the stored value to a displayable string value.

Examples:
// Split on comma, semi colon and any white space 
Converters.multivalue.getAsObject( value, /,|;|\s/ );

// Show values comma separated
Converters.multivalue.getAsObject( value, "," );
value is a global variable that's available in the conversion. For getAsObject it's the submitted string. For getAsString, it's the stored value.

Wednesday, June 16, 2010

XPages: Bug? View XPage Source code in the browser

Edit: This bug has been confirmed fixed in Domino 8.5.1 FP1.

I just read this in my daily RSS roundup.

Add a + after .xsp in the address field of your browser results in the source code of the current XPage being printed to the browser (click above link for screenshot or try for yourself). It's not sure how I feel about this. In most cases it won't be a security issue, but still..

Wednesday, June 2, 2010

XPages: Avoid saving duplicate documents on browser refresh

Update 22.06.10: Parameters are carried over when the redirect happens.
Update 02.09.10: Parameters starting with _ are stripped when redirect occurs

A reader wanted to know if there was some way to avoid duplicate documents when saving a document, refreshing the browser, and pressing ok on the resulting dialog. I wasn't able to recreate the problem of duplicate documents, but here's a workaround for the "POST" dialog. It should also take care of any duplicate documents on browser refresh.

In JSF lingo, this is known as the POST-Redirect-GET pattern. This is quite easy to accomplish in XPages.

On the save button, put the save action, and an Execute Script action. In the script part, call on the function below (put it in a SSJS Script Library/add it to the page). This makes the server redirect to the document in a get request, and you avoid the double posting problem.

function redirectToCurrentDocument( switchMode:boolean ){
try {
if( typeof currentDocument === 'undefined' || viewScope.stopRedirect ){ return; }
// Gets the name of the XPage. E.g. /Person.xsp
var page = view.getPageName();

// Finds extra parameters, strips away XPages parameters/trims leading &
var parameters = context.getUrl().getQueryString().substring(1);
var extraParameters = parameters.replace(
/([&\?^]*_[a-z0-9=]+[&]{0,1})|(action=\w{4}Document[\&]{0,1})|(documentId=[\w]{32}[\&]{0,1})/ig, '').replace( /\&$/, '' );

// Gets the unid of the current document
var unid = currentDocument.getDocument().getUniversalID();

// Sets/changes the action according according to the mode the document is in
var isEditable = currentDocument.isEditable();
if( switchMode ){ isEditable = !isEditable; } // false -> true / true -> false

var action = ( isEditable ) ? 'editDocument' : 'openDocument';

// Open the current document via a get request
var redirectUrl = page + '?documentId=' + unid + '&action=' + action;
if( extraParameters ){ redirectUrl += '&' + extraParameters; }
context.redirectToPage( redirectUrl );
} catch(e){ /* Exception logging */ }
}