{{Obsolete}}
Note: This topic has been added to the official documentation, and so this page is no longer maintained. The official documentation for Web Scripts in Alfresco 5.0 is here: http://docs.alfresco.com/5.0/concepts/ws-presentation-intro.html
Welcome to the Web Scripts WIKI, the home for all Web Script related documentation.
NOTE: This document describes features found in Alfresco v3 onwards (Older versions: v2)
Web Scripts allow you to:
You do not need tooling or Java knowledge. All you need is your favourite text editor or the Alfresco Explorer web client: no compilation, generators, server restarts, or complex installs.
Alfresco Web Scripts (introduced in 2006) brought together the worlds of content repository and the web. There's a blindingly obvious synergy between the two. Consider the web. It's one huge single collection of inter-related documents. Physically, the documents are located in many machines, in many data centers across the globe, stored and managed with many types of software. And yet, to the user in their browser it's one huge collection with easy to click links. This logical simplicity is all due to the underpinnings of the web; HTTP, URIs and HTML (and other types of document e.g. XML). So, what has this to do with ECM repositories. ECM Repositories are containers of documents, important documents that are the heart of Enterprises. However, ECM Repositories have traditionally provided proprietary access points therefore restricting potential use of those documents. This is made worse when ECM Repositories multiply within the Enterprise. Those proprietary access points make it difficult to link documents between repositories. But, wait a minute, users on their home PCs can access the world's huge collection of documents just using their browser. Why can't it be that easy within the Enterprise?
Traditional ECM Repository interfaces are APIs, a collection of programmer methods for interacting with the Repository. These APIs are barriers to accessing the documents held within the Repository. You can't get to the documents unless you have a tool that can speak the API. Documents are often identified by some kind of repository-scoped identifier. This style of interface is broadly known as RPC (remote procedure call). It turns out that RPC is not particularly suited to providing the capabilities of the web where distribution and uniform access of content is key. The web underpinnings of HTTP, URIs and media types follow a style known as REST, which stands for Representational State Transfer. Procedure calls are no longer central. Instead, we have uniquely identified Resources (document, feed, stock price, person) via URIs, representations of those resources (HTML, Atom, JSON) containing links to other resources and a small well-defined uniform interface for access (HTTP GET, POST, PUT & DELETE). So, until ECM Repositories become RESTful, it will be difficult to write applications and services that can access all ECM managed content distributed across the Enterprise. Think back to the web. The very essence of a uniform interface gave birth to Google (a search for the world's documents), Digg (a rating system for the worlds documents) and Delicious (a bookmarking and categorization system for the worlds documents). Within the Enterprise, content oriented services such as Transformation, Encryption, Workflow, Entitlement and Deployment can flourish with a uniform interface.
Web Scripts provide RESTful access to content held within your Alfresco Enterprise Content Repository. This allows you to place controls on your enterprise content to manage it, and at the same time, provide uniform access for a wide variety of client applications and services e.g. browser, portal, search engine or custom application. Due to the inherent distributed nature of this interface, all Alfresco Content Repositories within the Enterprise can resemble one logical collection of inter-related documents, just like the web. And as with any other web citizen, you can apply web technologies such as caching, authentication, proxies, negotiation etc. to your repository resources.
With Web Scripts, you can either build your own RESTful interface using light-weight scripting technologies such as JavaScript and Freemarker, allowing you to arbitrarily map any content in the Repository to resources on the web, or you can use pre-built out-of-the-box Web Scripts that already encapsulate many of the mappings. In fact, CMIS (a proposed standard for ECM web services) is implemented in Alfresco as a series of Web Scripts.
Since 2006, Web Scripts proved themselves exceedingly useful and have been used in all kinds of solutions:
A Web Script is simply a service bound to a URI which responds to HTTP methods such as GET, POST, PUT and DELETE. While using the same underlying code, there are broadly two kinds of Web Scripts.
Data Web Scripts encapsulate access and modification of content/data held in the repository therefore are provided and exposed only by the Alfresco Repository server. They provide a repository interface for client applications to query, retrieve, update and perform processes typically using document formats such as XML & JSON. Out-of-the-box, Alfresco provides a series of Data Web Scripts e.g. for tagging, activities, site management...
Presentation Web Scripts allow you to build user interfaces such as a dashlet for Alfresco Explorer or Alfresco Share, a portlet for a JSR-168 portal, a UI component within Alfresco SURF, a Web Site or a custom Application. They typically render HTML (and perhaps include browser hosted JavaScript). Unlike Data Web Scripts, Presentation Web Scripts may be hosted in the Alfresco Repository server or in a separate presentation server. When hosted separately, the Presentation Web Scripts interact with Data Web Scripts. Out-of-the-box, Alfresco provides a series of Presentation Web Scripts e.g. Portlets, Office Integration, SURF Components...
See the Web Scripts FAQ for more information.
The interface of a Web Script comprises of a URI, HTTP methods, and document types. This is all you need to know if you're just using a pre-built Web Script. However, if you want build your own RESTful interface, it has been made as simple as possible to construct your own Web Script. You do not need tooling or Java knowledge. All you need is your favourite text editor or Alfresco Explorer: no compilation, generators, server restarts, or complex installs.
A Web Script implementation consists of the following components (often summarized as MVC):
Advanced implementations may fall-back to Java. The different components (description doc, controller, and templates) are tied together through a document convention.
Web Scripts are registered and executed by the Web Scripts Framework.
Out-of-the-box, Alfresco provides several RESTful APIs (built as Web Scripts):
This section focuses on using pre-built Web Scripts. You will need to understand:
Basically, Web scripts are URI & HTTP accessible, so it is a case of putting together the correct HTTP request. Each Web Script is uniquely identified by its URI, although some Web Scripts may support multiple URIs.
Web Scripts may be used in a number of environments:
Web Script URIs are of the form:
http[s]://<host>:<port>/[<contextPath>/]/<servicePath>/<scriptPath>[?<scriptArgs>]
Where:
host - name or address of server hosting the Web Script
port - port where Web Script is exposed on server
contextPath - path where application is deployed to, generally /alfresco
servicePath - path where Web Script service is mapped to, generally /service
scriptPath - path to Web Script (as defined by Web Script)
scriptArgs - arguments to pass to Web Script
Some examples:
http://localhost:8080/alfresco/service/api/path/Workspace/SpacesStore/Company%20Home/children
http://localhost:8081/share/service/mytasks?priority=1
Depending on where a Web Script is invoked, the URI may be absolute or relative. In some cases Web Scripts may be deployed to the root application which means the contextPath is not required.
http://localhost:8080/service/api/path/Workspace/SpacesStore/Company%20Home/children
Some environments (such as Alfresco SURF and the Web Script Portal Runtime) only require the script path to identify a Web Script.
/api/path/Workspace/SpacesStore/Company%20Home/children
The servicePath is generally mapped to /service. However, the means for authenticating may be controlled via this path.
An index of all registered Web Scripts is available at the following URIs:
http://<host>:<port>/<contextPath>/<servicePath>/
http://<host>:<port>/<contextPath>/<servicePath>/index
Example:
http://localhost:8080/alfresco/service/index
http://localhost:8081/share/service/index
A description of each registered Web Script is provided including its URI(s) for calling the Web Script. Some URIs contain {tokens} - those are also valid URIs - they are expressed as URI Templates allowing for the URI path to be parameterized.
The index provides a view of Web Scripts by:
Also, if there happens to be an issue with the registration of a Web Script, the index also provides a list of Web Scripts that failed registration.
Note: This index page is itself implemented as a Web Script
Note: The Alfresco RESTful API Reference pages in this WIKI are just index pages rendered as MediaWIKI markup instead of HTML
Web Scripts may require the caller to be authenticated. Each Web Script dictates its own authentication requirements. If a Web Script requires authenticated access and the call is unauthenticated an appropriate authentication process is initiated. Upon successful authentication the Web Script is invoked.
The style of authentication depends on the environment within which the Web Script is hosted e.g. within a servlet run-time HTTP Auth may be used. Within a portal run-time, the portal may decide.
When Web Scripts are hosted within the Repository server two options are available for authentication.
The first is useful for external systems that wish to programmatically invoke Alfresco RESTfully (that is, using standard HTTP mechanisms), whereas the second is useful when single sign on is required across both the Alfresco Explorer Client and the Web Script, such as linking from the Explorer to a Web Script page.
The caller can choose which authentication option by specifying an appropriate <servicePath> in the Web Script URI.
Note: In both cases, the successful authentication may be remembered in the caller session, thus only requiring the authentication process to be initiated once
To force authenticated access specify the following URI argument:
alf_ticket=<ticket>
For example
http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home/children?alf_ticket=<ticket>
Tickets may be established by the calling environment (e.g. Alfresco SURF) or explicitly using the Login Web Script APIs.
Web scripts may allow guest access. To force guest access specify the following URL argument:
guest=true
For example
http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home?guest=true
Each Web Script returns its result in a reponse stream. The result may be encoded in one or more formats (i.e. mimetypes) such as HTML, XML or JSON depending on the capabilities of the invoked Web Script.
If a Web Script call does not provide any hints on which response encoding to use, the Web Script uses its pre-defined default encoding. However, a client can provide the following hints:
Hints 1 & 2 specify a Web Script Format which a registered short name for a mimetype. The typical mimetypes are mapped:
Hint 3 is typically provided by a Web Browser, although may be controlled programmatically via many HTTP client libraries and is specified as a comma-separated of mimetypes and preferences.
Note: Each Web Script dictates which of the above hints are adhered to via its Web Script Description
Not all clients can issue all HTTP methods. In the most severe case, a client may be restricted to GET and POST only. In this situation, web scripts provide two mechanisms for issuing any HTTP method via POST:
Note: If both the header and query parameter are specified in the request, the header takes precedence over the query parameter.
In either case, the value is set to the HTTP method name to be invoked.
For example, if the HTTP DELETE method is not supported by a client, but the client wishes to delete a repository folder, the following may be issued:
POST X-HTTP-Method-Override=DELETE http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home/
POST http://<host>:<port>/<contextPath>/<servicePath>/api/path/Workspace/SpacesStore/Company%20Home?alf_method=DELETE
Not all clients can gracefully handle non-success HTTP Responses, for example, the Flash runtime player - the runtime for Adobe Flex applications.
In this situation, web scripts provide a mechanism to force all HTTP Responses to indicate success in their Response Header. However, the Response body will still represent the content as if a non-success status had occurred.
To force HTTP Response Header success set the following HTTP Request Header:
If your web scripts provide JSON responses, it is likely they will be invoked directly from within a Web Browser via the XMLHttpRequest object. However, due to security reasons, you may run into cross-domain issues, a restriction that requires you to proxy your requests on the server side. Typically, to workaround these issues, public services such as Yahoo! JSON Services provide a callback mechanism.
Web scripts also provide this mechanism which wraps the JSON output text in parentheses and a function name of your choosing. A callback is invoked by adding the following URL parameter on JSON URL requests:
where function is the name of a client-side JavaScript function to be invoked.
The following simple HTML page demonstrates this:
This will result in the JSON response looking like this:
showTitle( ...json output... );
NOTE: For security reasons, this mechanism is disabled by default. To enable it on any SpringWebScripts container, set the bean property: allowCallbacks = true, for example:
<bean id='webscripts.container' parent='webscripts.abstractcontainer' class='org.springframework.extensions.webscripts.PresentationContainer'>
<property name='name'><value>Spring Web Scripts Container</value></property>
<property name='allowCallbacks'><value>true</value></property>
...
</bean>
Web Scripts comply with HTTP Caching supporting the notions of Last Modified Time and ETag allowing a Web Script response to be cached. A cache manager (suitable for the client environment) can interpret the cache controls (if any) returned with the Web Script response and cache appropriately.
A typical scenario is where a Repository-hosted Web Script is invoked via HTTP. In this case, any HTTP caching proxy may be used between the client and Alfresco Repository Server.
Note: Each Web Script dictates its caching controls, if any
This section demonstrates how to create a Web Script. The Web Script in question is a simple Hello World example requiring authenticated access which simply greets the user in both HTML and XML.
You will need to understand:
The only tools you'll need are an Alfresco Repository server and the Alfresco Explorer web client. Alternatively, advanced Web Scripts may be developed in Java.
Before diving into the technical bit, you may want to browse or refer to the available Web Scripts Examples.
If you're really impatient, you can create your first Web Script within the next minute or so.
You may also wish to refer to the Alfresco guidelines for designing RESTful Web Scripts.
First, choose a URI and HTTP method for your Hello World Web Script...
GET /<contextPath>/<servicePath>/sample/helloworld?to={name?}
The URI is under your control, but must be unique.
Note the following URL namespaces are reserved for Alfresco use:
/<contextPath>/<servicePath>/sample
/<contextPath>/<servicePath>/api
/<contextPath>/<servicePath>/ui
/<contextPath>/<servicePath>/office
/<contextPath>/<servicePath>/wcm
With a URI decided, it is necessary to bind a Web Script implementation to it, consisting of the following files:
Our example Hello World implementation will consist of the following files:
helloworld.get.desc.xml
helloworld.get.js
helloworld.get.html.ftl
helloworld.get.xml.ftl
helloworld.get.html.400.ftl
helloworld.get.xml.400.ftl
helloworld.get.config.xml
helloworld.get.properties
These files need to be stored in a folder somewhere. They can live in either the Alfresco Repository or the Java ClassPath. The following folders are listed in the sequence in which Alfresco searches for Web Script implementation files:
/Company Home/Data Dictionary/Web Scripts Extensions
/Company Home/Data Dictionary/Web Scripts
/alfresco/extension/templates/webscripts
/alfresco/templates/webscripts
Within any of these folders, you can use subfolders to organize web scripts.
New web scripts will not be visible until the 'Refresh Web Scripts' link is clicked on the /alfresco/service/ page.
Warning! do not store web scripts in:
/org/alfresco
or any of its subfolders - these folders are reserved for Alfresco use.
Our URI is described by creating an .xml description document in one of the above Web Script folders. For now, assume all files are placed into the repository folder: /Company Home/Data Dictionary/Web Scripts.
Within any Web Script folder, sub-folders may be used to organise Web Scripts. For example, we can place our Hello World Web Script in the folder
/Company Home/Data Dictionary/Web Scripts/org/alfresco/sample
and within this folder, create the file
helloworld.get.desc.xml
Naming is important. Web Scripts employ convention over configuration for their definition to reduce the amount of required typing. The postfix .desc.xml informs Alfresco that this is a Web Script description document.
The complete folder and file name
org/alfresco/sample/helloworld.get.desc.xml
defines
The content of the Hello World Web Script description document is defined as follows:
<webscript>
<shortname>Hello World</shortname>
<description>Greet a user</description>
<url>/helloworld?to={name?}</url>
<url>/hello/world?to={name?}</url>
<format default='html'>extension</format>
<authentication>user</authentication>
</webscript>
Where:
The description provides Alfresco with all the necessary information to bind the web script to one or more URIs. It also provides enough to document the Web Script.
Description documents also provide the following options, which are important, but not as frequently used or mandated.
An example of a full web script description follows:
<webscript kind='org.alfresco.httpsonly'>
<shortname>Hello World</shortname>
<description>Greet a user</description>
<url>/sample/helloworld?to={name}</url>
<url>/sample/helloworld.xml?to={name}</url>
<format default='html'>extension</format>
<lifecycle>sample</lifecycle>
<authentication runas='fred'>user</authentication>
<transaction>required</transaction>
<family>Sample</family>
<cache>
<never>false</never>
<public>false</public>
<mustrevalidate/>
</cache>
<negotiate accept='text/html'>html</negotiate>
<negotiate accept='text/xml'>xml</negotiate>
</webscript>
A URI template is simply a URI containing tokens which may be substituted with actual values. The URI template syntax is minimal and simple, and as of Alfresco v3, complies with the template syntax of JSR-311 (JAX-RS). Common forms of URI template include:
/content/a/b/c/d.txt
/sample/helloworld?to={name?}
/blog/search?q={searchTerm}&n={numResults}
/people/{personName}/profile/{profileStyle}
/blog/category/{category}?n={itemsperpage?}
/user/{userid}
A token of the form {<name>?} means it is optional. Typically, tokens are used for URI arguments, but may also be used for path segments.
Note the third example passes two parameters to the Web Script. Normally an ampersand (&) is used to separate parameters, though in your XML Web Script Description document one must use the escaped literal '&' instead.
For now, Web Scripts do not enforce mandatory arguments. However, it is still useful to provide a full URI template for documentation purposes.
URI templates are specified relative to http://<host>:<port>/<contextPath>/<servicePath>.
When a URI request is made, Alfresco locates the appropriate Web Script by finding the closest match to a URI template in a Web Script description document.
Given the following URI Templates:
1 /a
2 /a/b
3 /a/{val1}
4 /a/{val1}/b
5 /a/{val1}/b/{val1}
6 /a/{val1}/b/{val2}
The following URIs match the above templates as follows:
/a => template 1
/a/b => template 2
/a/c => template 3
/a/b/b => template 4
/a/c/b => template 4
/a/c/b/c => template 5
/a/c/b/d => template 6
However, the following URIs do not match any of the above templates:
/b
/a/c/c
As an implementor of a Web Script you have access to the values provided for tokens in the URI template e.g. access to the values for {val1} and {val2}. The values are available in both the Web Script Controller Script and Response Templates via the root object url.templateArgs.
When using java backed controllers the tokens can be accessed through the org.alfresco.web.scripts.WebScriptRequest.getServiceMatch()
API. In Community Edition 3.2r2 this will return a org.alfresco.web.scripts.Match
instance that has a getTemplateVars()
method returning a Map of all tokens.
A Web Script may optionally execute some JavaScript on invocation of its respective URI. The JavaScript can perform queries or updates against a back-end store. Also, the JavaScript may build a data model that is passed to the appropriate Web Script response template for subsequent response rendering.
For our example Hello World service we create the following file in the same folder as the Web Script description document
helloworld.get.js
Again, naming conventions apply where controller script file names have the following structure
<serviceId>.<httpMethod>.js
The following JavaScript root objects are available to all Web Scripts, regardless of their hosting environment.
Web Scripts hosted within the Alfresco Repository tier also have access to the following root objects which provide direct access to Repository services and content.
Web Scripts hosted within the presentation tier (i.e. within Alfresco SURF) have their own extras as described in the Surf Platform - Freemarker Template and JavaScript API.
For our example Hellow World, we can write the following JavaScript to either greet the currently authenticated user via their user name, or via the name provided as an URI argument:
model.toWho = (args.to != null) ? args.to : person.properties.userName;
The result of the script is added to the model root object and given the name who. Web Script response templates rendered after the execution of the JavaScript will now also have the root object named who, which in this case represents the name of the person to greet.
Of course, a Web Script can also perform updates, in which case, the model root object can be used to record items that have been updated, status, and so on.
The Alfresco Server provides a built-in Javascript debugger.
The final stage of a Web Script is to render a response for the HTTP request. Multiple response formats may be provided. Responses are rendered by FreeMarker templates.
For our example Hello World service, we create the following file in the same folder as the Web Script description document
helloworld.get.html.ftl
Again, naming conventions apply where response template file names have the following structure
<serviceId>.<httpMethod>.<format>.ftl
where
The following FreeMarker template root objects are available to all Web Scripts, regardless of their hosting environment.
Web Scripts hosted within the Alfresco Repository tier also have access to the following template root objects which provide direct access to Repository services and content.
Web Scripts hosted within the presentation tier (i.e. within Alfresco SURF) have their own extras as described in the Surf Platform - Freemarker Template and JavaScript API.
And remember, the FreeMarker template also has access to any root objects created by the controller script, if one has been associated with the Web Script.
Web script templates also have access to the following methods:
For our example blog search, we can write the following FreeMarker to render the HTML response:
At ${date?datetime}, ${person.properties.userName} says hello to ${toWho?html}
Note how the above template makes use of standard root objects, for example ${date?datetime} and root objects created by the controller script, for example ${toWho?html}.
Multiple response formats are simply supported by creating further template files following the naming convention outlined above.
Note: New and updated Templates are automatically registered and will be honoured when the Web Script is next invoked.
For our Hello World example, we can support add a machine-readable XML response by adding the following file
helloworld.get.xml.ftl
whose content is
<helloworld>
<from>${person.properties.userName}</from>
<says>hello</says>
<to>${toWho?xml}</to>
</helloworld>
With our Hello World example description document and associated templates, the following URI mappings are available:
Web Scripts use HTTP response status codes to:
Response status codes are set in the controller script via the status root object.
We can add a status to our Hello World example indicating that we can only greet one person at a time i.e. the 'to' argument is specified only once. We can use status code 400 (Bad Request) to represent this. The controller script is extended as follows:
if (argsM.to != null && argsM.to.length > 1)
{
status.code = 400;
status.message = 'Can only greet one person at a time.';
status.redirect = true;
}
else
{
model.toWho = (args.to != null) ? args.to : person.properties.userName;
}
Redirect allows the rendering of a custom response template for the specified status code. If redirect is not set (that is, false), the response header status code is set but the relevant standard web script response template is used, as would normally apply.
If redirect is true, then an appropriate status response template is searched for. The search is performed in the following order:
/org/alfresco/sample/helloworld.get.html.status.ftl
Status Response templates have access to the same root objects as standard Web Script response templates. The exception is that the default templates /<code>.ftl and /status.ftl only have access to the root objects 'url', 'status', 'server' and 'date'.
To provide a custom Bad Request status response for our Hello World example we create the following file in the same folder as the web script description document
helloworld.get.html.400.ftl
whose content is
${status.message}
We can also provide a meaningful response for machine-readable XML requests:
helloworld.get.atom.400.ftl
<response>
<code>${status.code}</code>
<codeName>${status.codeName}</codeName>
<codeDescription>${status.codeDescription}</codeDescription>
<message>${status.message}</message>
</response>
Remember, these templates are optional, as the search for an appropriate status template will always eventually find the default template /status.ftl.
Configuration is accessed via the config root object, which is available during both controller script and template execution.
There are 3 types of configuration, 'script', 'scoped' and 'global'. Script configuration is defined in an XML document with an arbitary structure stored locally with the Web Script. Global and scoped configuration are specified in Alfresco configuration files, as used in Alfresco Explorer.
Web Script configuration is read from an XML file packaged with the Web Script.
For our example Hello World service we create the following file in the same folder as the Web Script description document
helloworld.get.config.xml
Again, naming conventions apply where configuration file names have the following structure
<serviceId>.<httpMethod>.config.xml
The content of the configuration may be any valid XML.
<helloworld>
<greeting>hello</greeting>
<fromproperty>userName</fromproperty>
</helloworld>
Within a Controller Script, access to the configuration is via E4X which is essentially 'ECMAScript For XML' (tutorials can be found at http://www.w3schools.com/xml/xml_e4x.asp and/or http://wso2.org/library/1050, http://phpforms.net/tutorial/tutorial.html).
We can update our Hello World Controller script example to determine how to display 'who' the greeting is from.
model.toWho = (args.to != null) ? args.to : person.properties.userName;
var s = new XML(config.script);
model.fromWho = person.properties[s.fromproperty];
FreeMarker has builtin support for processing XML data allowing response templates direct access to configuration too. We can update our Hello World template example to extract the greeting from the configuration.
At ${date?datetime}, ${fromWho?html} says ${'''config.script.helloworld.greeting'''?html} to ${toWho?html}
Global and scoped config is read by the Alfresco Configuration Service. By default the following files are read:
Configuration sections that do not have an evaluator or condition are known as 'global' config sections i.e. these will always appear in a configuration lookup, a typical global configuration section has the following appearance.
<alfresco-config>
<config>
<server>
<errorpage>/jsp/error.jsp</errorpage>
<loginpage>/jsp/login.jsp</loginpage>
<guesthome enabled='true'>/jsp/guesthome.jsp</guesthome>
<url>/</url>
<url>/alf</url>
<url>/alfresco</url>
</server>
</config>
</alfresco-config>
Scoped configuration, on the other hand, is a configuration section that does have an evaluator and condition, for example.
<alfresco-config>
<config evaluator='string-compare' condition='Remote'>
<remote>
<endpoint>http://localhost:8080/alfresco</endpoint>
</remote>
</config>
</alfresco-config>
For changes in these files to take effect, the ALFRESCO server must be stopped and restarted (refreshing the webscripts is not sufficient).
Accessing the configuration above is achieved using the same techniques and syntax as any other model data, the global configuration is exposed via the config.global root object and the scoped config is exposed via the config.scoped root object.
For example to access the server configuration from the example above the following syntax would be used in a JavaScript:
var serverCfg = config.global.server;
and the following syntax would be used in a FreeMarker template:
<serviceId>.<httpMethod>.properties
Each line of the file represents a message identified by a unique key. The key may be dot separated.
Our example consists of:
at=At
says=says
to=to
greeting.hello=hello
greeting.howdy=howdy
Messages are accessed via the template method msg.
The Hello World HTML template is updated as follows:
${msg('at')} ${date?datetime}, ${fromWho?html} ${msg('says')} ${msg('greeting.' + config.script.helloworld.greeting)} ${msg('to')} ${toWho?html}
When posting a request of mimetype multipart/form-data the following additional root object is available to the Controller Script.
This root object allows a Web Script to read all fields included in the form including those of type 'file'. It provides a mechanism for iterating through each field and examining its meta-data such as name, value, content type etc.
All fields that are not of type 'file' are also mapped into the args and argsM root objects allowing access to fields by name.
View the File Upload Example to see how formdata is used to upload files into the Alfresco Repository.
A Controller Script can gain access to a request body allowing the processing of content posted to a Web Script.
By default, the following root object is provided:
As with formdata, the content may be converted to a string (if plausible) or written to an output stream such as a content object held in the Alfresco Repository.
Often, content is posted in a structured form such as XML or JSON. In these cases, the content can be converted to a string and subsequently parsed by the controller script. However, this can become cumbersome or error prone if the parsing is required by several Web Scripts implementations. To alleviate this problem, the Web Scripts framework provides the notion of a Format Reader which parses a request of a given mimetype into an object structure that is then automatically provided to the Controller Script.
Out-of-the-box, the Web Script framework provides the following Format Readers.
Format Readers are not invoked automatically i.e. sending a JSON request to a Web Script does not automatically provide a json root object to the Controller Script. The fall-back requestbody is provided instead.
To explicitly initiate a Format Reader requires a Controller Script whose name is structured:
<serviceId>.<httpMethod>.<format>.js
e.g.
folder.post.json.js => create 'json' root object for Controller Script when application/json mimetype is posted
folder.post.atomentry.js => create 'entry' root object for Controller Script when application/atom+xml;type=entry mimetype is posted
Runtime cache control is optionally specified via the root object cache which is available to the Web Script Controller script. At runtime, expiry criteria may be set, and definition-time controls may be overridden.
Sometimes, scripts and templates are not powerful enough to implement the required functionality behind a URI. For example, streaming content.
For these scenarios it is possible to bind an Alfresco Spring configured Java Bean into the Web Script. The Java Bean may take full control, or work in conjunction with the script and templates.
For reference, you may browse these Java samples.
To bind a Spring bean to a Web Script it is only necessary to create a bean with an id of the following structure:
id='webscript.<packageId>.<serviceId>.<httpMethod>'
and ensure its parent is the 'webscript' bean:
parent='webscript'
Note: <packageId> is the package (directory) that the Web Script descriptor (.desc.xml file) is located in, not the package of the Java class that implements the logic for the Web Script.
For example:
<bean id='webscript.org.alfresco.sample.helloworld.get'
class='my.java.package.structure.HelloWorld'
parent='webscript' />
Note that the bean id is made up of:
From this we can determine that the descriptor for this Web Script is called helloworld.get.desc.xml and is located in the directory org/alfresco/sample (either in the repository or in the classpath, as described in Deciding Where to place Web Script Implementation).
As with any Spring bean, other service(s) your Java class requires (including the Alfresco APIs) may be dependency injected into your Java bean.
The Java Class must implement the Java Interface:
org.alfresco.web.scripts.WebScript
which declares the following methods:
/**
* Gets the Service Description
*
* @return service description
*/
public WebScriptDescription getDescription();
/**
* Execute service
*
* @param req
* @param res
* @throws IOException
*/
public void execute(WebScriptRequest req, WebScriptResponse res)
throws IOException;
Abstract helper classes are also provided to simplify the development of a Java backed Web Script.
The first helper class is:
org.alfresco.web.scripts.AbstractWebScript
which allows a Web Script to take full control over the request. This is achieved by providing an implementation of the execute method. The helper class provides access to information such as the Web Script description document and methods for invoking JavaScript and templates.
The second helper class is:
org.alfresco.web.scripts.DeclarativeWebScript
which allows a web script to mix Java, JavaScript, and templates. This kind of Web Script supports all the behaviour described in this document, with the addition of first executing some Java. As with the controller script, the Java code can build a data model that is available to Response templates. The Java to be executed is placed into the executeImpl method.
A Java implemented web script also has support for response status codes. There are two approaches to sending a response code:
All exceptions thrown from a Web Script are caught by the Web Script Runtime and converted to response status codes. By default, the status code 500 (Internal Error) is used. However, the exception WebScriptException supports the constructor:
public WebScriptException(int statusCode, String message)
This constructor is used to explicitly set a response code in an error situation, for example
if (req.getParameter('q') == null || req.getParameter('q').length() == 0)
{
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, 'Search term not provided');
}
Using this approach will always render the relevant status response template.
The helper class DeclarativeWebScript provides the method executeImpl whose signature is:
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status, Cache cache)
The status argument may be used to explicitly set a response status code & message, as well as control the redirect to a status response template, for example
if (req.getParameter('q') == null || req.getParameter('q').length() == 0)
{
status.setCode(HttpServletResponse.SC_BAD_REQUEST);
status.setMessage('Search term not provided');
status.setRedirect(true);
return;
}
Exceptions may be handled as follows:
catch(AuthenticationException e)
{
status.setCode(HttpServletReponse.SC_FORBIDDEN);
status.setMessage('Invalid username & password');
status.setException(e);
status.setRedirect(true);
}
By default, Alfresco Web Scripts support an MVC style of implementation. Sometimes, it's useful to override the default behavior to add an extra capability, or provide a completely different style of Web Script such as those that need to stream large amounts of data back on the response. This can be done by developing a Java-backed implementation, however, it cannot be re-used across several Web Scripts without changes to Spring configuration.
A Web Script 'Kind' allows a Java-backed implementation to be named, and for any Web Script Description Document to state it wants to use that implementation instead of the out-of-the-box one provided by Alfresco. See list of currently available Web Script kinds.
The steps for creating a new 'Kind' are:
The steps here are exactly the same as for any other Java-backed implementation as described above.
The backing bean may be configured via extensions to the Web Script Description Document. To access those extensions it is necessary to provide an implementation of the interface:
org.alfresco.web.scripts.DescriptionExtension
which defines the following method:
/**
* Gets the custom description extensions
*
* @param serviceDescPath path to service doc
* @param serviceDesc service doc input stream
* @return extensions mapped by name
*/
public Map<String, Serializable> parseExtensions(String serviceDescPath, InputStream servicedesc);
The method parses the complete Description Document (XML), extracts its extensions and places them into a map (indexed by the name of extension) which is returned. It is called when the Web Script is registered with the Web Script framework.
Spring is used to register the Description Extension implementation:
<bean id='webscriptdesc.my.kind.of.web.script' class='my.kind.of.WebScriptDocumentExtension'/>
The id is the same as the Web Script backing bean except it starts with webscriptdesc instead of webscript.
To gain access to your extension values within your backing bean you can use the following method which returns the map created above:
getDescription().getExtensions()
That's it. To use your new 'Kind' of Web Script refer to Advanced Description Options. Note: The id referenced in the kind attribute is specified without the webscript. prefix.
New and updated Web Scripts can be registered without restarting the Alfresco server. This makes it easy to use the Alfresco Repository to develop Web Scripts.
To register a Web Script:
The 3.0 Web Scripts Framework provides logging of Web Script registration and execution.
Various tools are available for testing Web Scripts.
Web Scripts that are not Java-backed may be exported from one Repository and imported to another via the simple Web Script Dump and Load utility.
The utility is also useful for viewing the complete implementation of a Web Script in a single page, allowing it to be copied and sent via e-mail or posted to the forums for sharing or diagnosing a problem.
The resource bundle /alfresco/messages/webscripts.properties contains status code names and descriptions for all HTTP codes.
Learn more about
A host object providing access to multipart/form-data requests allowing file upload from a web script. See example.
Note: You must use enctype='multipart/form-data' in your form tag or the formdata variable will not be populated with fields.
A host object providing access to a form field within a multipart/form-data request.
A host object providing access to the URL (or parts of the URL) that triggered the web script.
For example, imagine a web script URL template of
/user/{userid}
and a web script request URL of
/alfresco/service/user/fred?profile=full&format=html
The url root object will respond as follows:
You can reuse the web clients authentication by using the /alfresco/wcservice/ URL to reference you web script. Change the URL to /alfresco/service/ and you will get an HTTP authentication prompt even if you are logged in in the Alfresco web client. The first option is the most convenient for the end user, the second may be useful while testing with different accounts.
An associative array of response status properties that allow control over the status and content of the Web Script response.
An associative array of cache control properties that allow control over how the Web Script response is cached. See example.
The format of the response.
If the content is JSON then an object called 'json' is added to the model.
The json object is either a JSONArray or JSONObject depending upon whether the content is an array or not.
jsonUtils allows access to the json data from the controller script.
These are the commonly used methods of JSONArray.
These are the commonly used methods of JSONObject.
TODO:
An associative array of meta-data properties that describe the hosting Alfresco server.
An associative array of meta-data properties describing the web script.