Web Scripts Examples

From AlfrescoWiki

Jump to: navigation, search


Back to Web Scripts.


NOTE: This document describes features to be found in Alfresco v2.1 onwards.


Contents

[edit] Introduction

This page presents some sample Web Scripts that reside in your Alfresco Server at the folder:

/Company Home/Data Dictionary/Web Scripts/org/alfresco/sample

[edit] Quick start

You can run some of these scripts right now on a clean install of Alfresco 2.1 or later.

If you installed by following Installing on Microsoft Windows or Linux Quick Install then you can access the folder webscript here:

http://localhost:8080/alfresco/service/sample/folder/Company%20Home 

Log in with the username admin and password admin.

[edit] What Is All This Information?

For each Sample described below, you see the following information:

URL
Defines the format of the URL used to access the script
Examples
Gives concrete examples of valid URLs to call the script. You can call the web script by copying the URL into your browser and substituting your host name and port.

Description Document

Execute Script

Response Template

These are source of the content items in Alfresco, found by browsing to the following space:

Company Home > Data Dictionary > Web Scripts > org > alfresco > sample

The file names and extensions are important as they tell Alfresco what the item is for.

If you want to experiment with your first web script see this page: Web Scripts Hello World Quick Start

[edit] Hello World

This sample very simply demonstrates how to create a polite Alfresco Repository.

URL
GET /alfresco/service/sample/hello

Examples:

http://<host>:<port>/alfresco/service/sample/hello
Description Document
File: hello.get.desc.xml
<webscript>
  <shortname>Hello</shortname>
  <description>Polite greeting</description>
  <url>/sample/hello</url>
  <authentication>user</authentication>
</webscript>
Response Templates
File: hello.get.html.ftl
Hello ${person.properties.userName}.

[edit] Folder Browse/RSS Feed

This sample demonstrates how to implement a URL service that renders the contents of a folder in either HTML or ATOM. The HTML rendition allows for the browsing of a folder hierarchy.

URL
GET /alfresco/service/sample/folder/{path}

Examples:

http://<host>:<port>/alfresco/service/sample/folder/Company%20Home
http://<host>:<port>/alfresco/service/sample/folder/Company%20Home?format=atom
Description Document
File: folder.get.desc.xml
<webscript>
  <shortname>Folder Listing Sample</shortname>
  <description>Sample demonstrating the listing of folder contents</description>
  <url>/sample/folder/{path}</url>
  <format default="html">argument</format>
  <authentication>guest</authentication>
  <transaction>required</transaction>
</webscript>
Execute Script
File: folder.get.js
// locate folder by path
var folder = roothome.childByNamePath(url.extension);
if (folder == undefined || !folder.isContainer)
{
   status.code = 404;
   status.message = "Folder " + url.extension + " not found.";
   status.redirect = true;
 }
 model.folder = folder;
Response Templates
File: folder.get.html.ftl
<html>
  <head>
    <title>${folder.displayPath}/${folder.name}</title>
  </head>
  <body>
    Folder: ${folder.displayPath}/${folder.name}
    <br>
    <table>
     <#if folder.parent.parent?exists>
     <tr>
       <td><td><a href="${url.serviceContext}/sample/folder<@encodepath node=folder.parent/>">..</a>
     </tr>
     </#if>
<#list folder.children as child>
     <tr>
       <#if child.isContainer>
         <td>><td><a href="${url.serviceContext}/sample/folder<@encodepath node=child/>">${child.name}</a>
       <#else>
         <td><td><a href="${url.serviceContext}/api/node/content/${child.nodeRef.storeRef.protocol}/${child.nodeRef.storeRef.identifier}/${child.nodeRef.id}/${child.name?url}">${child.name}</a>
       </#if>
     </tr>
</#list>
    </table>
  </body>
</html>
<#macro encodepath node><#if node.parent?exists><@encodepath node=node.parent/>/${node.name?url}</#if></#macro>
File: folder.get.atom.ftl
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator version="${server.version}">Alfresco (${server.edition})</generator>
  <title>Folder: ${folder.displayPath}/${folder.name}</title> 
  <updated>${xmldate(date)}</updated>
  <icon>${absurl(url.context)}/images/logo/AlfrescoLogo16.ico</icon>
<#list folder.children as child>
  <entry>
    <title>${child.name}</title>
    <#if child.isContainer>
      <link rel="alternate" href="${absurl(url.serviceContext)}/sample/folder<@encodepath node=child/>"/>
    <#else>
      <link rel="alternate" href="${absurl(url.serviceContext)}/api/node/content/${child.nodeRef.storeRef.protocol}/${child.nodeRef.storeRef.identifier}/${child.nodeRef.id}/${child.name?url}"/>
    </#if>
    <icon>${absurl(url.context)}${child.icon16}</icon>
    <id>urn:uuid:${child.id}</id>
    <updated>${xmldate(child.properties.modified)}</updated>
    <summary>${child.properties.description!""}</summary>
    <author> 
      <name>${child.properties.creator}</name>
    </author> 
  </entry>
</#list>
</feed>
<#macro encodepath node><#if node.parent?exists><@encodepath node=node.parent/>/${node.name?url}</#if></#macro>

[edit] WCM Stores List

This sample demonstrates how to implement a URL service that renders a list of all WCM stores.

URL
GET /alfresco/service/sample/avm/stores

Examples:

http://<host>:<port>/alfresco/service/sample/avm/stores
Description Document
File: avmstores.get.desc.xml
<webscript>
  <shortname>AVM Stores Sample</shortname>
  <description>Sample demonstrating the listing of AVM stores</description>
  <url>/sample/avm/stores</url>
  <format default="html"/>
  <authentication>admin</authentication>
  <transaction>required</transaction>
</webscript>
Response Templates
File: avmstores.get.html.ftl
<html>
  <head>
    <title>AVM Stores</title>
  </head>
  <body>
    AVM Stores
    <br>
    <br>
    <table>
     <tr>
<#list avm.stores as store>
     <tr>
        <td>${store.creator}<td>&nbsp;<td>${store.createdDate?datetime}<td>&nbsp;<td><a href="${url.serviceContext}/sample/avm/path/${store.id}/">${store.id}
     </tr>
</#list>
    </table>
  </body>
</html>

[edit] WCM Folder Browse

This sample demonstrates how to implement a URL service that renders the contents of a WCM folder in HTML. The rendition allows for the browsing of a folder hierarchy.

URL
GET /alfresco/service/sample/avm/path/{storeid}/{path}

Examples:

http://<host>:<port>/alfresco/service/sample/avm/path/main--admin/www
Description Document
File: avmbrowse.get.desc.xml
<webscript>
  <shortname>AVM Browse Sample</shortname>
  <description>Sample demonstrating the listing of AVM folder contents</description>
  <url>/sample/avm/path/{storeid}/{path}</url>
  <format default="html">argument</format>
  <authentication>admin</authentication>
  <transaction>required</transaction>
</webscript>
Execute Script
File: avmbrowse.get.js
script:
{
   // extract avm store id and path
   var fullpath = url.extension.split("/");
   if (fullpath.length == 0)
   {
     status.code = 400;
     status.message = "Store id has not been provided.";
     status.redirect = true;
     break script;
   }
   var storeid = fullpath[0];
   var path = (fullpath.length == 1 ? "/" : "/" + fullpath.slice(1).join("/"));
    
   // locate avm node from path
   var store = avm.lookupStore(storeid);
   if (store == undefined)
   {
     status.code = 404;
     status.message = "Store " + storeid + " not found.";
     status.redirect = true;
     break script;
   }
   var node = avm.lookupNode(storeid + ":" + path);
   if (node == undefined)
   {
     status.code = 404;
     status.message = "Path " + path + " within store " + storeid + " not found.";
     status.redirect = true;
     break script;
   }
    
   // setup model for templates
   model.store = store;
   model.folder = node;    
}
Response Templates
File: avmbrowse.get.html.ftl
<html>
  <head>
    <title>AVM Folder: ${folder.displayPath}/${folder.name}</title>
  </head>
  <body>
    <a href="${url.serviceContext}/sample/avm/stores">AVM Store</a>: ${store.id}
    <br>
    <br>
    AVM Folder: ${folder.displayPath}/${folder.name}
    <br>
    <br>
    <table>
     <#if folder.parent?exists>
     <tr>
      <td>${folder.parent.properties.creator}<td>&nbsp;<td>${folder.parent.size}<td>&nbsp;<td>${folder.parent.properties.modified?datetime}<td>&nbsp;<td><td><a href="${url.serviceContext}/sample/avm/path/${store.id}<@encodepath node=folder.parent/>">..</a>
     </tr>
     </#if>
<#list folder.children as child>
     <tr>
       <#if child.isContainer>
        <td>${child.properties.creator}<td>&nbsp;<td>${child.size}<td>&nbsp;<td>${child.properties.modified?datetime}<td>&nbsp;<td>><td><a href="${url.serviceContext}/sample/avm/path/${store.id}<@encodepath node=child/>">${child.name}</a>
       <#else>
         <td>${child.properties.creator}<td>&nbsp;<td>${child.size}<td>&nbsp;<td>${child.properties.modified?datetime}<td>&nbsp;<td><td><a href="${url.serviceContext}/api/node/content/${child.nodeRef.storeRef.protocol}/${child.nodeRef.storeRef.identifier}/${child.nodeRef.id}/${child.name?url}">${child.name}</a>
       </#if>
     </tr>
</#list>
    </table>
  </body>
</html>

[edit] Blog Search

This sample demonstrates how to implement a URL service that performs a full-text search whose results are rendered in either HTML or ATOM.

URL
GET /alfresco/service/sample/blog/search?q={searchTerm}

Examples:

http://<host>:<port>/alfresco/service/sample/blog/search?q=alfresco
http://<host>:<port>/alfresco/service/sample/blog/search.atom?q=alfresco
Description Document
File: blogsearch.get.desc.xml
<webscript>
  <shortname>Blog Search Sample</shortname>
  <description>Sample that finds all blog entries whose content contains the specified search term</description>
  <url>/sample/blog/search?q={searchTerm}</url>
  <url>/sample/blog/search.atom?q={searchTerm}</url>
  <url>/sample/b/s?q={searchTerm}</url>
  <url>/sample/b/s.atom?q={searchTerm}</url>
  <format default="html">extension</format>
  <authentication>guest</authentication>
  <transaction>required</transaction>
</webscript>
Execute Script
File: blogsearch.get.js
// check that search term has been provided
if (args.q == undefined || args.q.length == 0)
{
   status.code = 400;
   status.message = "Search term has not been provided.";
   status.redirect = true;
}
else
{
   // perform search
   var nodes = search.luceneSearch("TEXT:" + args.q);
   model.resultset = nodes;
}

NOTE: The above script executes a simple full-text search across the whole repository. It could be extended to search within a specific folder or a specific type of content.

Response Templates
File: blogsearch.get.html.ftl
<html>
  <body>
    <img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" />
    Blog query: ${args.q}
    <br>
    <table>
<#list resultset as node>
     <tr>
       <td><img src="${url.context}${node.icon16}"/>
       <td><a href="${url.serviceContext}/api/node/content/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/${node.name?url}">${node.name}</a>
     </tr>
</#list>
    </table>
  </body>
</html>
File: blogsearch.get.atom.ftl
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator version="${server.version}">Alfresco (${server.edition})</generator>
  <title>Blog query: ${args.q}</title> 
  <updated>${xmldate(date)}</updated>
  <icon>${absurl(url.context)}/images/logo/AlfrescoLogo16.ico</icon>
<#list resultset as node>
  <entry>
    <title>${node.name}</title>
    <link rel="alternate" href="${absurl(url.serviceContext)}/api/node/content/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/${node.name?url}"/>
    <icon>${absurl(url.context)}${node.icon16}</icon>
    <id>urn:uuid:${node.id}</id>
    <updated>${xmldate(node.properties.modified)}</updated>
    <summary>${node.properties.description!""}</summary>
    <author> 
      <name>${node.properties.creator}</name>
    </author> 
  </entry>
</#list>
</feed>
Status Template
File: blogsearch.get.html.404.ftl
<html>
  <body>
    ${status.message}
  </body>
</html>
File: blogsearch.get.atom.404.ftl
<?xml version="1.0" encoding="UTF-8"?>
<response>
  ${status.code}
  <codeName>${status.codeName}</codeName>
  <codeDescription>${status.codeDescription}</codeDescription>
  <message>${status.message}</message>
</response>

[edit] Blog Category Search

This sample demonstrates how to implement a URL service that performs a category search whose results are rendered in either HTML or ATOM.

Content may be categorized via the Alfresco Web Client (Content Properties page).

URL
GET /alfresco/service/sample/blog/category/{category}

Examples:

http://<host>:<port>/alfresco/service/sample/blog/category/EUROPE
Description Document
File: categorysearch.get.desc.xml
<webscript>
  <shortname>Category Search</shortname>
  <description>Find all blog entries tagged with specified categories</description>
  <url>/sample/blog/category/{category}</url>
  <format default="html">extension</format>
  <authentication>guest</authentication>
  <transaction>required</transaction>
</webscript>
Execute Script
File: categorysearch.get.js
// check category exists?
var category = search.luceneSearch("PATH:\"/cm:generalclassifiable//cm:" + url.extension + "\"");
if (category == undefined)
{
   status.code = 404;
   status.message = "Category " + url.extension + " not found.";
   status.redirect = true;
}
else
{
   // perform category search
   var nodes = search.luceneSearch("PATH:\"/cm:generalclassifiable//cm:" + url.extension + "//member\"");
   model.resultset = nodes;
}

NOTE: The above script executes a simple category search across the whole repository. It could be extended to search within a specific folder or a specific type of content.

Response Templates
File: categorysearch.get.html.ftl
<html>
  <body>
    <img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" />
    Category search: ${url.extension}
    <br>
    <table>
<#list resultset as node>
     <tr>
       <td><img src="${url.context}${node.icon16}"/>
       <td><a href="${url.serviceContext}/api/node/content/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/${node.name?url}">${node.name}</a>
     </tr>
</#list>
    </table>
  </body>
</html>
File: categorysearch.get.atom.ftl
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator version="${server.version}">Alfresco (${server.edition})</generator>
  <title>Category search: ${url.extension}</title> 
  <updated>${xmldate(date)}</updated>
  <icon>${absurl(url.context)}/images/logo/AlfrescoLogo16.ico</icon>
<#list resultset as node>
  <entry>
    <title>${node.name}</title>
    <link rel="alternate" href="${absurl(url.serviceContext)}/api/node/content/${node.nodeRef.storeRef.protocol}/${node.nodeRef.storeRef.identifier}/${node.nodeRef.id}/${node.name?url}"/>
    <icon>${absurl(url.context)}${node.icon16}</icon>
    <id>urn:uuid:${node.id}</id>
    <updated>${xmldate(node.properties.modified)}</updated>
    <summary>${node.properties.description!""}</summary>
    <author> 
      <name>${node.properties.creator}</name>
    </author> 
  </entry>
</#list>
</feed>
Status Templates
File: categorysearch.get.html.404.ftl
<html>
  <body>
    ${status.message}
  </body>
</html>
File: categorysearch.get.atom.404.ftl
<?xml version="1.0" encoding="UTF-8"?>
<response>
  <code>${status.code}</code>
  <codeName>${status.codeName}</codeName>
  <codeDescription>${status.codeDescription}</codeDescription>
  <message>${status.message}</message>
</response>

[edit] File Upload

This sample (split into two web scripts) demonstrates how to implement a file upload URL service. (sorry, available 2.1 Enterprise onwards)

The first web script presents a simple HTML form with three form fields, one of which is of type file. The form POSTs to a second web script which parses the posted multipart/form-data request and creates a new file in the repository using the values entered into the form.

[edit] Upload Form

URL
GET /sample/upload
Description Document
File: upload.get.desc.xml
<webscript>
  <shortname>File Upload Form Sample</shortname>
  <description>Form for uploading file content and meta-data into Repository</description>
  <url>/sample/upload</url>
  <authentication>user</authentication>
</webscript>
Response Templates
File: upload.get.html.ftl
<html>
 <head> 
   <title>Upload Web Script Sample</title> 
   <link rel="stylesheet" href="${url.context}/css/main.css" TYPE="text/css">
 </head>
 <body>
   <table>
     <tr>
       <td><img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" /></td>
       <td><nobr>Upload Web Script Sample</nobr></td>
     </tr>
     <tr><td><td>Alfresco ${server.edition} v${server.version}
   </table>
   <p>
   <table>
     <form action="${url.service}" method="post" enctype="multipart/form-data" accept-charset="utf-8">
       <tr><td>File:<td><input type="file" name="file">
       <tr><td>Title:<td><input name="title">
       <tr><td>Description:<td><input name="desc">
       <tr><td><td>
       <tr><td><td><input type="submit" name="submit" value="Upload">
     </form>
   </table>
 </body>
</html>

[edit] Upload Script

URL
POST /sample/upload
Description Document
File: upload.post.desc.xml
<webscript>
  <shortname>File Upload Sample</shortname>
  <description>Upload file content and meta-data into Repository</description>
  <url>/sample/upload</url>
  <authentication>user</authentication>
</webscript>
Execute Script
File: upload.post.js
var filename = null;
var content = null;
var title = "";
var description = "";

// locate file attributes
for each (field in formdata.fields)
{
  if (field.name == "title")
  {
    title = field.value;
  }
  else if (field.name == "desc")
  {
    description = field.value;
  }
  else if (field.name == "file" && field.isFile)
  {
    filename = field.filename;
    content = field.content;
  }
}

// ensure mandatory file attributes have been located
if (filename == undefined || content == undefined)
{
  status.code = 400;
  status.message = "Uploaded file cannot be located in request";
  status.redirect = true;
}
else
{
  // create document in company home for uploaded file
  upload = companyhome.createFile("upload" + companyhome.children.length + "_" + filename) ;
  upload.properties.content.write(content);
  upload.properties.encoding = "UTF-8";
  upload.properties.title = title;
  upload.properties.description = description;
  upload.save();
 
  // setup model for response template
  model.upload = upload;
}
Response Templates
File: upload.post.html.ftl
<html>
 <head> 
   <title>Upload Web Script Sample</title> 
   <link rel="stylesheet" href="${url.context}/css/main.css" TYPE="text/css">
 </head>
 <body>
   <table>
     <tr>
       <td><img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" /></td>
       <td><nobr>Upload Web Script Sample</nobr></td>
     </tr>
     <tr><td><td>Alfresco ${server.edition} v${server.version}
     <tr><td><td> 
     <tr><td><td>Uploaded <a href="${url.serviceContext}/sample/folder${upload.displayPath}">${upload.name}</a> of size ${upload.properties.content.size}.
   </table>
 </body>
</html>

[edit] URL Argument Handling

This sample demonstrates how to process URL arguments (single and multi-valued).

URL
GET /alfresco/service/sample/args

Examples:

http://<host>:<port>/alfresco/service/args?a=2&a=1&b=3
Description Document
File: args.get.desc.xml
<webscript>
 <shortname>Argument Handling Sample</shortname>
 <description>Demonstrate access to single and multi-valued arguments</description>
 <url>/sample/args</url>
 <authentication>none</authentication>
</webscript>
Execute Script
File: args.get.js
// log each argument (assuming only one value has been provided for each)
for (arg in args)
{
  logger.log(arg + "=" + args[arg]);
}

// log each argument (assuming one or more values have been provided for each)
for (arg in argsM)
{
  for each (val in argsM[arg])
  {
     logger.log(arg + "=" + val);
  }
}

Response Templates
File: args.get.html.ftl
<#list args?keys as arg>
  ${arg}=${args[arg]}
</#list>

<#list argsM?keys as arg>
<#list argsM[arg] as val>
  ${arg}=${val}
</#list>
</#list>

[edit] Cache Control

This sample demonstrates how to specify Cache control at definition-time and run-time. (sorry, available 2.1 Enterprise onwards)

URL
GET /alfresco/service/sample/cache

Examples:

http://<host>:<port>/alfresco/service/sample/cache
Description Document
File: cache.get.desc.xml
<webscript>
 <shortname>Cache Sample</shortname>
 <description>Demonstrates how to control caching for a Web Script</description>
 <url>/sample/cache</url>
 <authentication>guest</authentication>
 <format default="text"/>
 <cache>
  <never>false</never>
  <public/>
 </cache>
</webscript>
Execute Script
File: cache.get.js
var tutorial = companyhome.childByNamePath("/Guest Home/Alfresco-Tutorial.pdf");
model.tutorial = tutorial; 
cache.ETag = tutorial.properties.description;
cache.lastModified = tutorial.properties.modified;
Response Templates
File: cache.get.text.ftl
${tutorial.properties.description}

[edit] 3xx Status Code Redirection

This sample demonstrates how to support redirection for the 3xx family of status codes. (available in 2.1.2 Enterprise onwards)

URL
GET /alfresco/service/sample/location

Examples:

http://<host>:<port>/alfresco/service/sample/location
Description Document
File: location.get.desc.xml
<webscript>
 <shortname>Status Location</shortname>
 <description>Demonstrates how to handle 3xx redirection status codes</description>
 <url>/sample/3xxLocation</url>
 <authentication>none</authentication>
</webscript>
Execute Script
File: location.get.js
status.code = 307;  // Temporary redirect
status.location = url.service + "/temp";
Response Templates
File: location.get.html.ftl
This resource you're looking for is at <a href="${status.location}">${status.location}</a>.

[edit] Associating Web Script with Custom View

This functionality will not have any user interface in 2.1, but you can execute a very simple JavaScript via URL to set a web script as a nifty custom view for a space:

Web Script Custom View Example #1
// example script to set the Document List webscript as the custom view for a node 
// @param noderef    space to set webscript against as custom view 
var dest = search.findNode("<noderef>"); 
dest.addAspect("cm:webscriptable"); 
dest.properties["cm:webscript"] = "/service/ui/doclist?q=*"; 
dest.save();


Web Script Custom View Example #2

You may wish to pass context to your web script to inform it which space or document it is processing against. This can be done as follows:

// example script to set the Document List webscript as the custom view for a node 
// @param noderef    space to set webscript against as custom view 
var dest = search.findNode("<noderef>"); 
dest.addAspect("cm:webscriptable"); 
dest.properties["cm:webscript"] = "/service/ui/doclist?nodeRef={noderef}"; 
dest.save();

Your web script will now receive an argument with the name nodeRef whose value is the node reference of the space or document to which the web script is being applied. The following code demonstrates how you can use this argument within your web script to retrieve the node:

<#assign nodeRef = args["nodeRef"]>
<#assign node = companyhome.nodeByReference[nodeRef]>

Your web script can then proceed to business as usual with code like:

The name of this space is ${node.name}

and so forth.

[edit] JSON example

When you are using JSON, it seems that you cannot stringify native Java objects without having problems with stack overflows. So what you can do is to create your own Javascript objects which wrap around the native Java objects. This object could then be "stringified" and sent to the Ajax client.

Here is a sample web script, which uses JSON for marshalling a JavaScript object:

/*
   json.js
(modified 2006-09-07): added support for RHINO
(modified 2006-05-02): json.parse, json,stringify added
  USAGE:
  var jsObj = JSON.parse(jsonStr);
  var jsonStr = JSON.stringify(jsObj);
*/
if (!this.json) {
  var json = (function () {
      var m = {
              '\b': '\\b',
              '\t': '\\t',
              '\n': '\\n',
              '\f': '\\f',
              '\r': '\\r',
              '"' : '\\"',
              '\\': '\\\\'
          },
          s = {
              array: function (x) {
                  var a = ['['], b, f, i, l = x.length, v;
                  for (i = 0; i < l; i += 1) {
                      v = x[i];
                      f = s[typeof v];
                      if (f) {
                          v = f(v);
                          if (typeof v == 'string') {
                              if (b) {
                                  a[a.length] = ',';
                              }
                              a[a.length] = v;
                              b = true;
                          }
                      }
                  }
                  a[a.length] = ']';
                  return a.join();
              },
              'boolean': function (x) {
                  return String(x);
              },
              'null': function (x) {
                  return "null";
              },
              number: function (x) {
                  return isFinite(x) ? String(x) : 'null';
              },
              object: function (x) {
                  if (x) {
  
                      if (x instanceof Array) {
                          return s.array(x);
                      }
                      if (x.hashCode) return s.string(+x.toString());
  
                      var a = ['{'], b, f, i, v;
                      for (i in x) {
                          v = x[i];
                          f = s[typeof v];
                          if (f) {
  
                              v = f(v);
  
                              if (typeof v == 'string') {
                                  if (b) {
                                      a[a.length] = ',';
                                  }
                                  a.push(s.string(i), ':', v);
                                  b = true;
                              }
                          }
                      }
                      a[a.length] = '}';
                      return a.join();
                  }
                  return 'null';
              },
              string: function (x) {
                  if (/["\\\x00-\x1f]/.test(x)) {
                      x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                          var c = m[b];
                          if (c) {
                              return c;
                          }
                          c = b.charCodeAt();
                          return '\\u00' +
                              Math.floor(c / 16).toString(16) +
                              (c % 16).toString(16);
                      });
                  }
                  return '"' + x + '"';
              }
          };
  
     return {
        parse: function(s) {
           try {
              return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
                 s.replace(/"(\\.|[^"\\])*"/g, ))) &&
                 eval('(' + s + ')');
           } catch (e) {
              return false;
           }
        },
        stringify: s.object
     };
  })();
}
var WEBSERVICE_PATH = '/com/onepoint/alfresco/webservices/folderbrowser';
/**
 * This is a Javascript object that can be used
 * for JSON serialization.
 * @param folder The Java object that is going to be transformed into a Javascript object.
 */
function JsonFolder(folder) {
  this.displayPath = folder.displayPath;
  this.name        = folder.name;
  this.hasParent   = folder.parent.parent != null;
  this.parentPath  = escape(encodePath(folder.parent));
  this.folders     = new Array();
  this.files       = new Array();
  this.fileLinks   = new Array();
  
  for(var i = 0, length = folder.children.length; i < length; i++) {
     var child = folder.children[i];
     if (child.isContainer) {
        this.folders[this.folders.length] = escape(encodePath(child));
     }
     else {
        this.files[this.files.length] = escape(encodePath(child));
        var childName = child.name != null ? child.name : "";
        this.fileLinks[this.files.length] = escape(url.serviceContext + "/api/node/content/"
           + child.nodeRef.storeRef.protocol + "/" + child.nodeRef.storeRef.identifier + "/"
           + child.nodeRef.id + "/" + childName);
     }
  }
}
/**
 * Encodes the path of one node.
 * @param node The node that is to be encoded.
 */
function encodePath(node) {
  var path = "";
  if(node.parent) {
     path = node.name;
     path = encodePath(node.parent) + '/' + path;
  }
  return path;
}
// locate folder by path
if ((args.p == null || args.p.length == 0) && (url.extension == null || url.extension.length == 0))
{
  status.code = 400;
  status.message = "No folder has been specified.";
  status.redirect = true;
}
else {
  var folder = roothome.childByNamePath(args.p != null ? args.p : url.extension);
  model.folder = folder;
  var jsonFolder = new JsonFolder(folder);
  model.jsonFolder = json.stringify(jsonFolder);
}

The JSON functions (for example, stringify) were downloaded from http://www.cosmocode.de/en/blogs/detman/json_rhino_pitfalls/ and were slightly modified. This page contains an interesting blog article about JSON and RHINO. On the client side, you can use without problems the Javascript library, which is available at http://www.json.org .

[edit] Webscripts in dialogs

See this page for how to easily create dialogs displaying web scripts. Using this mechanism, the creation of a dialog is only the definition of a corresponding well-configured action.

[edit] Java-backed Web Scripts Samples

A Wiki page is available that documents several Java-backed Web Scripts Samples which were developed during the Alfresco Barcelona Community Conference in 2008. This covers the concepts as well as development and code. It provides a link to an AMP file that you can use to quickly see how these are put together and deployed.