MindTouch ships with jQuery, which is a versatile JavaScrpt library for doing a lot of things. Primarily, it focuses on DOM element manipulation, making it easier to create compelling, dynamic pages. But jQuery also includes AJAX functionality for interacting with web-services, such as the MindTouch API.
Most of the jQuery AJAX functions aren't that interesting, because they are only intended for the most basic cases. For this write-up, we're more interested in some of the advanced use cases that require both a good understanding of AJAX and the MindTouch API. For sake of concreteness, the samples show how to interact with page properties for creating, retrieving, updating, and deleting values.
Things to know before you begin making your AJAX request:
Lastly, it needs to be pointed out that the samples use JEM, which is a JavaScript pre-processor that's built into MindTouch. JEM makes it easier to mix DekiScript and JavaScript on wiki pages, and react to user input, such as button clicks. JEM is only available in MindTouch Core 9.02 and MindTouch 2009, or later. However, the shown jQuery functionality has been available since MindTouch 8.08.
Use the POST:pages/{pageid}/properties API to create a new property. The API expects the name of the new property to be supplied in a custom header named Slug.
You can use any name you like, as long as it's non-empty. It's recommended that you use a prefix for your property as well. In our examples, we call our property "sample-property" and we use the custom namespace prefix "urn:custom.mindtouch.com". The full property name then becomes: "urn:custom.mindtouch.com#sample-property" (w/o the quotes). Using the custom namespace prefix allows our property to be shown on the "Page Properties" page.
Unless you know what you're doing, it's recommended you use the "urn:custom.mindtouch.com" prefix as well!
The following button will try to create a new property on the current page. The operation will fail if the property already exists or if you do not have permission to create a property.
Here's the code for showing a button to create a property.
<input type="button" value="Click me to create a property." ctor="when($this.click) {
// send AJAX request using jQuery
$.ajax({
// set the uri for the properties API of the current page
url: {{ page.api }} + '/properties',
// set the request HTTP verb
type: 'POST',
// set the value of the new property
data: 'This is the new value!',
contentType: 'text/plain',
processData: false,
// add the 'Slug' header which sets the property name
beforeSend: function(xhr) {
xhr.setRequestHeader('Slug', 'urn:custom.mindtouch.com#sample-property');
return true;
},
// check outcome of the request
complete: function(xhr) {
// check the response status code
if(xhr.status == 200) {
alert('Property was successfully created.');
} else {
alert('Unable to create the property. It may already exist (try deleting it) or you don\\'t have permission to create it.');
}
}
});
}" />
To read a property, we need to query the properties collection using the GET:pages/{pageid}/properties API. We limit the set of returned properties by using the names query parameter.
Also, to make our lives easier, we indicate to the API that we want the result to be returned as a JSON object instead of an XML document. Once the property is read, we display it an alert message.
The following button queries the properties of the current page and tries to find the property created by the previous sample. If the property is found, it's current value is displayed in an alert box.
Here's the code for showing a button to read a property.
<input type="button" value="Click me to read a property" ctor="when($this.click) {
// property name to read
var name = 'urn:custom.mindtouch.com#sample-property';
// send AJAX request using jQuery
$.ajax({
// set the uri for the properties API of the current page
// add parameter to only list properties that match the requested name
// add parameters to convert response to JSON and
url: {{ page.api }} + '/properties?dream.out.format=json&names=' + Deki.url.encode(name),
// set the request HTTP verb
type: 'GET',
cache: false,
// check outcome of the request
complete: function(xhr) {
// check response status code
if(xhr.status == 200) {
// evaluate response JSON data
var data = eval('(' + xhr.responseText + ')');
// read property value
var value = data.property && data.property.contents['#text'];
// check we the value was found
if(value) {
alert('Property was successfully read.\\nIts value is \\'' + value + '\\'');
} else {
alert('Unable to find the property. It may not exist (try creating it).');
}
} else {
alert('Unable to query the property. You don\\'t have permission to read it.');
}
}
});
}" />
To update a property, we must first read it to obtain its location and ETag. The update operation is then performed using the PUT:pages/{pageid}/properties/{key} API. The ETag value is provided as a HTTP header, similar to the Slug header when we created the property.
The ETag ensure that we don't update a value that may have changed since we read it. In other words, it provides a mechanism for detecting a conflict in updating a property. How the conflict should be handled depends on the situation. For example, if the property contains a list of values, and the intent was to append another value, then simply re-read the property, re-append the value, and try to update it again.
The following button will try to read the property and its ETag and then update its value. The operation will fail if the property does not exist, you do not have the permission to update it, or it has been modified by someone else between the time it was read and the update attempt was made.
Here's the code for showing a button to update a property.
You may notice that the update operation is done using a POST instead of a PUT. The reason is that many browser only support GET and POST for AJAX requests. However, the MindTouch API allows you to specify the HTTP verb in the URI when doing a POST request to circumvent this limitation.
<input type="button" value="Click me to update a property" ctor="when($this.click) {
// property name to update
var name = 'urn:custom.mindtouch.com#sample-property';
// send AJAX request to find the property
$.ajax({
// set the uri for the properties API of the current page
// add parameter to only list properties that match the requested name
// add parameters to convert response to JSON and
url: {{ page.api }} + '/properties?dream.out.format=json&names=' + Deki.url.encode(name),
// set the request HTTP verb
type: 'GET',
cache: false,
// check outcome of the request
complete: function(xhr) {
// check response status code
if(xhr.status == 200) {
// evaluate respone JSON data
var data = eval('(' + xhr.responseText + ')');
// read property href and ETag
var href = data.property && data.property.contents['@href'];
var etag = data.property && data.property['@etag'];
// check we the value was found
if(href && etag) {
// send AJAX request to update the property
$.ajax({
// set the request HTTP verb
url: href + '?dream.in.verb=PUT',
type: 'POST',
// set the value of the updated property
data: 'This is the updated value!',
contentType: 'text/plain',
processData: false,
// add the 'ETag' header which checks if the property was modified since we read it
beforeSend: function(xhr) {
// NOTE (see Update 1 below): remove content-encoding added by Apache mod_deflate
etag = etag.replace('-gzip', '').replace('-bzip2', '').replace('-zip', '');
xhr.setRequestHeader('ETag', etag);
return true;
},
// check response status code
complete: function(xhr) {
// check the response status code
if(xhr.status == 200) {
alert('Property was successfully updated.');
} else {
alert('Unable to update the property. It may no longer exist (try recreating it), it may have been changed, or you don\\'t have permission to update it.');
}
}
});
} else {
alert('Unable to find the property. It may not exist (try creating it).');
}
} else {
alert('Unable to query the property. You don\\'t have permission to read it.');
}
}
});
}" />
Finally, we can delete a property using the DELETE:pages/{pageid}/properties/{key} API. The delete request does not require an ETag since it really doesn't matter if the property has changed since it was read. However, we still need to find its location.
The following button will delete the property created by the first sample. The operation will fail if the property does not exist or if you don't have permission to delete it.
Here's the code for showing a button to delete a property.
<input type="button" value="Click me to delete a property" ctor="when($this.click) {
// property name to update
var name = 'urn:custom.mindtouch.com#sample-property';
// send AJAX request to find the property
$.ajax({
// set the uri for the properties API of the current page
// add parameter to only list properties that match the requested name
// add parameters to convert response to JSON and
url: {{ page.api }} + '/properties?dream.out.format=json&names=' + Deki.url.encode(name),
// set the request HTTP verb
type: 'GET',
cache: false,
// check outcome of the request
complete: function(xhr) {
// check response status code
if(xhr.status == 200) {
// evaluate respone JSON data
var data = eval('(' + xhr.responseText + ')');
// read property href
var href = data.property && data.property.contents['@href'];
// check we the value was found
if(href) {
// send AJAX request to delete the property
$.ajax({
// set the request HTTP verb
url: href + '?dream.in.verb=DELETE',
type: 'POST',
// check response status code
complete: function(xhr) {
// check the response status code
if(xhr.status == 200) {
alert('Property was successfully deleted.');
} else {
alert('Unable to delete the property. It may not exist (try creating it) or you don\\'t have permission to delete it.');
}
}
});
} else {
alert('Unable to find the property. It may not exist (try creating it).');
}
} else {
alert('Unable to query the property. You don\\'t have permission to read it.');
}
}
});
}" />
Use the POST:pages/{pageid}/security API to set new security permissions. The API expects the permission in XML format, so we will have to play with JQuery to create this XML.
This XML allows for a expiration date for each permissions set.
The following button will try to add a new security restriction on the current page. The operation will fail if the restriction already exists or if you do not have permission to add a security resitriction.
Here's the code for showing a button to add a security restriction.
<input type="button" value="Set Page Security" ctor="
$this.click(function(){
var userid = {{ user.id }};
var dateexpires = {{ date.addMinutes(date.now,5) }};
var page = {{ page.path }};
var restriction= 'Semi-public';
// generate the details
var restrictions = Deki.$('<security></security>');
$('<permissions.page></permission.page>')
.append(Deki.$('<restriction>' + restriction + '</restriction>'))
.appendTo(restrictions);
var grantsAdded = Deki.$('<grants.added></grants.added>').appendTo(restrictions);
$('<grant></grant>')
.append(Deki.$('<permissions></permissions>')
.append(Deki.$('<role>Contributor</role>')))
.append(Deki.$('<user></user>').attr('id',userid))
.append(Deki.$('<date.expires></date.expires>').text(dateexpires))
.appendTo(grantsAdded);
restrictions = '<security>' + restrictions.html() + '</security>';
var pageapi = '/@api/deki/pages/=' + Deki.url.encode(Deki.url.encode('DekiScript/FAQ/How_do_I..._Use_jQuery_to_access_the_MindTouch_API_via_AJAX%3f/Page_Security_example'));
// send AJAX request using jQuery
$.ajax({
// set the uri for the properties API of the current page
url: pageapi + '/security',
// set the request HTTP verb
type: 'POST',
contentType: 'text/xml',
processData: true,
// set the value of the new property
data: restrictions,
// check outcome of the request
complete: function(xhr) {
// check the response status code
if(xhr.status == 200) {
alert('Page property was set successfully.');
} else {
alert('Unable to create the security. Perhaps you do not have permission.');
}
}
});
});
" />
Hopefully these samples have given you a solid starting point for using the MindTouch API with AJAX requests. The samples show all the best practices you should follow when making your own AJAX requests, such as relying on asynchronous callback functions instead of blocking calls. The latter make for a very poor user experience quickly (though, it may not be apparent during the development cycle, so just trust me on that!). All response codes are taken into consideration, although one could validly argue that just checking for status code 200 is too strict. And finally, jQuery's AJAX below-the-covers smarts, such as automatic content transformations and caching, are disabled when needed.
Next, you should read on the full capabilities the ajax() function in jQuery, as well as the MindTouch API. If you run into any trouble or have something cool to share, make sure to post it on the DekiScript & Deki Extensions forum.
User:rberinger has reported an issue on a thread on the forums. It turns out that Apache servers with mod_deflate installed may modify the value of the ETag by appending the Content-Encoding value to the ETag. For example, if the ETag value was originally "abc" and the Content-Encoding is "gzip", you will end up with an ETag having the value "abc-gzip". Needless to say, this then makes the update operation fail since the ETag doesn't match anymore.
The workaround is to strip "-gzip", "-bzip2", and "-zip" from the end of an ETag when they are present.
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by