UNDER CONSTRUCTION
What is a widget
A widget is a single html page that is served from the web server. Within the web page is normally contained all the HTML necessary to make the widget work, and the widget will link to a css file and a javascript file to make the widget work. Ideally a widget will not contain any embedded css or javascript.
Everything that is recommended in this manual, is only recommendations and as long as the behaviour of the widget is correct with respect to the portal containing the widget, you can choose any javascript libraries you want to, these are only our recommendations based on the widgets we have developed so far. We do not want to restrict innovation by our choices.
Structure of a Widget
- Widgets have a name
- the name should be unique amongst all widgets.
- the name should describe the function of the widget
- the name of the widget will not be exposed, unless the widget decides to do so
- Widgets live within their own folder within the same server as Sakai
- this enables a widget to perform AJAX operations back to the sakai instance.
- Within the widget folder there will be an html page that contains the widget
- There will be a css folder, a js folder and optionally an images folder.
- During development you will use local copies of javascript and css contained within the widget development kit.
Creating your first widget.
In this example we will create a widget called HelloWorld, hosted at webapps/widgets/HelloWorld
1. Create the Folder Structure
- /widgets
- /widgets/HelloWorld
- /widgets/HelloWorld/js/helloworld.js
- /widgets/HelloWorld/css/helloworld.css
- /widgets/HelloWorld/images
- /widgets/HelloWorld/HelloWorld.html
2. Add the Widget Development Kit
Copy the widgetdev.js file into the root folder you have created (not the javascript folder). This file contains library functions that will make it easier to perform standard widget operations without having to duplicate code.
- /widgets/HelloWorld/widgetdev.js
3. Create an html page for the widget.
Initially during development you can use a full html page, but once the widget is created you will need to strip the html page down to a fragment containing just the body tag of the html page and <script> and <link> tags for the supporting javascript and css. You should use absolute paths excluding the server name and port to reference your javascript and css.
Below is the stripped version ready for deployment.
<!-- style sheet for hello world, note that the element is of the form <link></link> and not <link/> --> <link href="/widgets/HelloWorld/css/helloworld.css" rel="stylesheet" type="text/css"></link> <!-- standard widget development library --> <script type="text/javascript" src="widgetdev.js"></script> <!-- it's a good idea to use a container around all markup, then the css can scope to it --> <div class="helloworld_container" > <p id="helloworld_message" > <a href="#" onclick="HelloWorld.click(); return false; " >Hello World</a> </p> </div> <!-- this is a template that will be used in rendering dynamic output. TrimPath templates should be enclosed in a comment that starts immediately after the containing div start tag. This is so that they can be found as the first child of the div element. --> <div id="helloworld_template1" style="display:none" ><!-- Your first widget was clicked at ${timenow} --> </div> <!-- javascript for hello world --> <script type="text/javascript" language="JavaScript" src="/widgets/HelloWorld/js/helloworld.js"></script>
Some points to note.
- All the references to javascript and css files are absolute, so when the widget is loaded into the portal page, the links will still point to the correct place.
- link and script tags contain both start and end elements e.g. <link></link> and not just the single tag e.g. <link />
- ids are prefixed with the name of the widget e.g. helloworld_message and helloworld_template1 to avoid collisions with other widgets.
- javascript is namespaced e.g. HelloWorld.clickHelloWorld()
- never use absolute possitioning as this will overlay the portal page and your widget will appear in the wrong place.
- if you use float layout remember to place this inside the control of the widget and not allow items to float over the whole page.
The accompanying css and javascript is as follows
/* CSS is scoped using the outer container to avoid collisions with other widgets */
div.helloworld_container #hellowworld_message {
border: 1px solid #ccc;
}
/** -------------------------------------------------------------------------------------- * Javascript must be namespaced to limit the number of global variables and avoid clashes, you should use * one namespace per widget and encapsulate all your code within that template. In this instance we have used * a function to construct the namespace, but it's perfectly acceptable to use a static declaration of the form * var HelloWorldStatic = { * click : function() { * // what is the date now ? * var datenow = new Date(); * var context = { * timenow : datenow * }; * // evaluate the template in div helloworld_template1 with context * var r = SData.Template.render("helloworld_template1",context); * * // append the result to the div helloworld_message * document.getElementById("helloworld_message").innerHTML += r; * } * * } * -------------------------------------------------------------------------------------- */ var HelloWorld = function() { return { click = function() { // what is the date now ? var datenow = new Date(); var context = { timenow : datenow }; // evaluate the template in div helloworld_template1 with context var r = SData.Template.render("helloworld_template1",context); // append the result to the div helloworld_message document.getElementById("helloworld_message").innerHTML += r; } }; }();
If this widget was deployed to the server it would be your first Hello World Widget. Nothing more is needed to write a widget. Just simple HTML, Javascript and CSS; leaving you free to think about design, usability and accessibility.
Data Services
For the widget to do anything meaningful it will need to interact with the Sakai Server. We do this using REST based calls to services on the Sakai SData web application. These services return JSON which can be evaluated into Javascript objects in the browser with a minimum of overhead. You can write your own JSON web applications, but we are trying to provide a generic set of functionality in SData that covers 80% of the needs of a widget developer, so hopefully you will not have to resort to writing your own JSON service.
To get information from a server-side service you use an AJAX call. We have implemented a simple supporting AJAX service that makes this operation easier. The code for a simple GET is given below
SData.ajax({
url : "/sdata/motd",
onSuccess : function(data) {
var context = eval( '(' + data + ')' );
// evaluate the template in div helloworld_template1 with context
var r = SData.Template.render("helloworld_motd_template1",context);
// append the result to the div helloworld_message to display the message of the day
document.getElementById("helloworld_message").innerHTML += r;
},
onFail : function(status) {
alert("Oh dear no message of the day server said "+status);
}
});
This code performs a GET on the Message of the Day service, renders the result with a template called helloworld_motd_template1 and appends to the div helloworld_message. If the AJAX call fails it will alert the user with a message.
A slightly more complex example is shown below, uploading a file to the personal storage service.
var DemoSaveFile = { saveFile : function(fileName, fileContents){ if (path && path.length > 0 && fileContents != null) { // data can contain many items each item that contains a data, fileName and contentType member will // be sent as a file upload in the multipart form-data post var data = {"items": {"data": fileContents, "fileName": fileName, "contentType": "text/plain"}}; SData.ajax({ url : "sdata/p/mysavespace", httpMethod : "POST", onSuccess : function(data){ alert("Save Sucessfull to "+fileName); }, postData : data, contentType : "multipart/form-data" }); } } }
Here is a brief description of the available data services in SData.
| Url | Description |
|---|---|
| /sdata/mcp | List of courses and projects of the currently logged in user. |
| /sdata/me | Information about the currently logged in user. |
| /sdata/mff?search=query | Search for files that contain the query. |
| /sdata/mgs?search=query&siteId=siteid/all. | Search for everything that contains the query within the current site or with in all your sites. |
| /sdata/motd | Returns the Message of the Day. |
| /sdata/mra | A list of what recently happened within my worksites. |
| /sdata/site?siteid=siteid | Get info about a specific site. |