Saturday, June 21, 2008

YUI Treeview & Grails

In a project I need a kind of CMS in order to store/retrieve large files. Basically my choices were narrowed down to Alfresco and Grails.
Alfresco was seen as the "low entry cost" solution, Grails requiring more work but being more flexible.
Obviously my main requirement was to be able to upload large files (>10 GB). Obviously this can not be done using the browser and a specific (but simple) client had to be designed. Testing this with Grails took me one day, many tutorials being available [1][2]. With Alfresco despite many post to the forum, I am still stuck to the 2GB limit ...

I decided to go on with Grails. My next step was to be able to visualize the data being stored on the server. For this I decided to use the YUI Treeview component. Here is the controller I used:


// show.gsp will be used
def show = {
}

def dir = {
def cmsId = params.cms
def dir = params.dir?:"/"

// Removing some characters from the path
dir = dir.replaceAll("\\.\\./","")

// Get the root dir for the CMS
String configDir = grailsApplication.config.cmsdata.dir

String target = configDir + cmsId + dir

def files = []
File d = new File(target)
if (d.exists() && d.isDirectory()){
files = d.listFiles()
}

// return back a JSON structure
response.setHeader("Cache-Control", "no-store")
render(contentType: "text/json") {
nodes {
for (f in files) {
node(name: f.name, isDir: f.isDirectory())
}
}
}
}


The view is really simple. When you click on a tree node, the path is constructed the controller is called, the JSON is parsed and new tree nodes are created dynamically differentiating leaf nodes. (Here is the jscript part).




(function(){

// Creating the tree
function buildTree() {
var tree;
tree = new YAHOO.widget.TreeView("treeDiv");

tree.setDynamicLoad(loadNodeData);

var root = tree.getRoot();

var tempNode = new YAHOO.widget.TextNode("/", root, false);

tree.draw();
}

// Processing new nodes
function loadNodeData(node, fnLoadComplete) {
var path = "";
var leafNode = node;
while (!leafNode.isRoot()) {
path = leafNode.label + "/" + path;
leafNode = leafNode.parent;
}

var nodelabel = encodeURI(path);

var sUrl = "${params.cms}/dir?dir="+nodelabel;

var callback = {
success: function(oResponse){
var oResults = YAHOO.lang.JSON.parse(oResponse.responseText);

if ((oResults.nodes)&&(oResults.nodes.length)){
if (YAHOO.lang.isArray(oResults.nodes)) {
for (var i=0; i < oResults.nodes.length; i++){
var tempNode = new YAHOO.widget.TextNode(oResults.nodes[i].name, node, false);
tempNode.isLeaf = !oResults.nodes[i].isDir;
}
}
}
oResponse.argument.fnLoadComplete();
},
failure: function(oResponse){
oResponse.argument.fnLoadComplete();
},
argument:{
"node": node,
"fnLoadComplete":fnLoadComplete
},
timeout:5000
}

YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
};

YAHOO.util.Event.onDOMReady(buildTree);
}());



If you want to work offline (as I did), you have to install the excellent YUI Plugin.

The next step for me is to package this in a YUI panel :)