XWikiJSONWebServiceKickStart
XWiki JSON Web Service kick start
This tutorial addresses the development of a small Web Service in XWiki, that returns data formated in the JSON format. Our service will offer a timeline of last X edited documents on the wiki on which it runs, returning for each document the date and author of last edit.
Data format
Here is a sample of two rows to illustrate how we will format our data, using a JSON representation
{"returneddocs": 2,
"docs": [
{"fullName" : "Music.RageOverTheLostPenny",
"url" : "http://www.domain.tld/xwiki/bin/view/Music/RageOverTheLostPenny",
"author" : "XWiki.LudwigVonBeethoven",
"date" : "2008-07-24 16:13:06.0",
"comment": "Corrected wrong info about the story of this piece of music of mine"
},
{"fullName" : "Movies.ScienceOfSleep",
"url" : "http://www.xwiki.org/xwiki/bin/view/Movies/ScienceOfSleep",
"author" : "XWiki.JohnDoe",
"date" : "2008-07-24 16:09:06.0",
"comment": "Added a new image"
}
]
}
The Web Service
Our web service will be held in a wiki page : Main.WebService, that would become queryable by any http client. It is written in Velocity. If you need a particular inquiry that the searchdocuments do not fit your ncessity, so you need re write the code below in Groovy.
{{velocity}}
$response.setContentType("application/x-json")
##limit default
#set ($limit = 5)
#if($request.limit)
#set ($limit = $util.parseInt($request.limit))
#end
#set($hql = "where doc.name != null order by doc.date desc")
#set($documents= $xwiki.searchDocuments($hql, $limit, 0))
{"nbarticles": $limit ,
"documents": [
#set ($i = 0)
#foreach($item in $documents)
#set ($i = $i+1)
#set ($recentDoc = $xwiki.getDocument($item).getTranslatedDocument())
{"fullName": "$recentDoc.fullName",
"url": "$recentDoc.getExternalURL("view",$recentDoc.context)",
"author": "$recentDoc.contentAuthor",
"date": "$recentDoc.date",
"comment": "$recentDoc.comment"
}#if($i != $limit)
,
#end
#end
]
}
{{/velocity}}PoC client in XWiki
Now our Web Service is available for any http client. Usually Web Services are a great deal for different applications to exchange data, but they can also be used within the same application. For illustration purpose, we will present a small javascript client in a wiki page that regularly queries the service with an AJAX request, and evaluate and inject in the DOM the returned JSON result to offer a small "live-monitoring" table of editions on the wiki. In a real-world usage, the client could for example be a different application of the information system, that would need to be aware of the latest edition in a wiki.
Version 1, without using skin extension
Code
{{velocity}}
{{html}}
10 Last edited documents
<table id="lastestedits" class="wiki-table">
<thead id="tableheader">
<tr>
<td>document</td>
<td>author</td>
<td>date</td>
<td>comment</td>
</tr>
</thead>
</table>
<script type="text/javascript">
// <[!CDATA[[
function monitorWikiDocsEdition()
{
window.setTimeout('monitorWikiDocsEdition()', 5000); // Timeout to keep looping over the service every 5 seconds.
new Ajax.Request(
// Here we make the asynchronous call to our previously created web service.
// Note that we are using the XWiki API from velocity to compute the exact URL we want to hit.
// This is possible since the entire page will be evaluated by the velocity engine before being sent to
// the client from which this javascript function will be executed.
// We precise the number of entries we want to retrieve using the limit parameter. The second parameter,
// "outputSyntax" is mandatory.If we forget this parameter, we will not receive a valid JSON
// and our client will not work.
"$xwiki.getURL('Main.WebService', 'get', $escapetool.url({'outputSyntax': 'plain','limit': 10}))",{
method: 'get',
onSuccess: function(transport) {
// first, we evaluate the JSON result to make it manipulable as javascript objects
var res = eval( '(' + transport.responseText + ')' );
// then we iterate over the returned elements, and inject them dynamically in the table
for(var i = 0 ;i <res.documents.length;i++) {
doc = res.documents[i];
var tdDoc = new Element('td').update(doc.fullName);
var tdAuthor = new Element('td').update(doc.author);
var tdDate = new Element('td').update(doc.date);
var tdComment = new Element('td').update(doc.comment);
var tr = new Element('tr', {'id' : doc.fullName});
tr.appendChild(tdDoc);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdComment);
// if this document already exist in the table, we remove it
if($(doc.fullName))
$(doc.fullName).remove();
// if the table is already full, we remove its last row
if($('lastestedits').getElementsBySelector("tr")[10])
$('lastestedits').getElementsBySelector("tr")[10].remove();
// finally, we inject the row on top of the table
new Insertion.After('tableheader', tr);
}
}
}
);
}
monitorWikiDocsEdition();
// ]>
</script>
{{/html}}
{{/velocity}}
Result
As a result, we will have a table presenting last 10 editions on the wiki, and that updates live when new editions does occur :

Version 2, using SX
Create a page, for example. Main.ClientWebService
Add a object XWiki.JavaScriptExtension
Input the following code:
function monitorWikiDocsEdition()
{
window.setTimeout('monitorWikiDocsEdition()', 5000); // Timeout to keep looping over the service every 5 seconds.
new Ajax.Request(
// Here we make the asynchronous call to our previously created web service.
// Note that we are using the XWiki API from velocity to compute the exact URL we want to hit.
// This is possible since the entire page will be evaluated by the velocity engine before being sent to
// the client from which this javascript function will be executed.
// We precise the number of entries we want to retrieve using the limit parameter. The second parameter,
// "outputSyntax" is mandatory.If we forget this parameter, we will not receive a valid JSON
// and our client will not work.
"$xwiki.getURL('Main.WebService', 'get', $escapetool.url({'outputSyntax': 'plain','limit': 10}))",{
method: 'get',
onSuccess: function(transport) {
// first, we evaluate the JSON result to make it manipulable as javascript objects
var res = eval( '(' + transport.responseText + ')' );
// then we iterate over the returned elements, and inject them dynamically in the table
for(var i = 0 ;i <res.documents.length;i++) {
doc = res.documents[i];
var tdDoc = new Element('td').update(doc.fullName);
var tdAuthor = new Element('td').update(doc.author);
var tdDate = new Element('td').update(doc.date);
var tdComment = new Element('td').update(doc.comment);
var tr = new Element('tr', {'id' : doc.fullName});
tr.appendChild(tdDoc);
tr.appendChild(tdAuthor);
tr.appendChild(tdDate);
tr.appendChild(tdComment);
// if this document already exist in the table, we remove it
if($(doc.fullName))
$(doc.fullName).remove();
// if the table is already full, we remove its last row
if($('lastestedits').getElementsBySelector("tr")[10])
$('lastestedits').getElementsBySelector("tr")[10].remove();
// finally, we inject the row on top of the table
new Insertion.After('tableheader', tr);
}
}
}
);
}
monitorWikiDocsEdition();
Parameters of the java script extension:
On Demand
Parse: Yes
Cache: default
In other page, for exameple, Main.ClientPageWebService, input the following code:
{{velocity}}
{{html}}
10 Last edited documents
<table id="lastestedits" class="wiki-table">
<thead id="tableheader">
<tr>
<td>document</td>
<td>author</td>
<td>date</td>
<td>comment</td>
</tr>
</thead>
</table>
<script type="text/javascript">
$xwiki.jsx.use("Main.ClientWebService")
{{/html}}
{{/velocity}}
You have a nice webservice in your XWiki!