eXo Platform APIs¶
This chapter includes introduction to eXo Platform APIs overall, and tutorials that help you get familiar with some APIs in particular.
Definitions of API Levels Information on APIs’ level.
Platform API A list of APIs at Platform level:
Java API
REST API
JavaScript API
Web Services
Provisional API A list of API at Provisional level.
Working with eXo REST APIs Tutorials that help you develop your eXo REST client in Java/JavaScript.
Portal API Tutorials helping you to retrieving, creating and deleting Portal Sites and Portal Pages.
Definitions of API Levels¶
APIs vary according to the maturity level. It is important to understand the eXo Platform’s general approach to the API change management. The different levels of API are described in the following table:
API Level |
Test Suite |
Clients |
Documentation |
Support |
Compatibility X.Y.Z(1) |
Compatibility X.Y(1) |
---|---|---|---|---|---|---|
Platform API |
||||||
Provisional API |
||||||
Experimental API |
Best effort |
Best effort |
||||
Unsupported API |
Test Suite: A suite of tests that can be run against the API to detect changes.
Clients: The API has been used successfully by at least 2 different teams, using the API Documentation only.
Documentation: The API has a clean JavaDoc and reference documentation.
Support: The eXo Support team provides help on the code that uses this API, and fixes any reported bugs.
Compatibility X.Y.Z(1): The compatibility between maintenance versions (X.Y.Z and X.Y.Z1) is guaranteed. If there is any change between X.Y and X.Y1, the eXo Support team will help by upgrading the code.
Compatibility X.Y(1): The compatibility between minor versions (X.Y and X.Y1) is guaranteed. If there is any change between X and X1, the eXo Support team will help by upgrading the code.
Best Effort: You will receive assistance, but eXo Platform cannot guarantee any specific result.
Use Provisional or Experimental API
These APIs are provided to give an “early look” at which will be available in upcoming versions of eXo Platform. These APIs are not final, but they can be used to start developing your application.
Provisional API are APIs close to being frozen, but that need a last look from users. They can be used by third-party developers for their own apps, with the knowledge that only a limited compatibility guarantee is offered.
Experimental API are APIs that are likely to change. They are published to get feedback from the community. These API have been tested successfully, but have not yet had enough feedback from developers.
Platform API¶
This section summarizes a list of eXo Platform API which can be categorized into:
Java API
A Java API documentation is now available in a Javadoc format and regularly updated to provide developers with in-depth information about new features in eXo Platform.
See Java API Documentation 5.1 for more details.
REST API
eXo Platform provides REST APIs and resources to developers who want to integrate eXo services into their applications.
See REST API Documentation 5.0 for more details.
JavaScript API
`OpenSocial 2.5.1 Gadget Specification: <http://opensocial.github.io/spec/2.5.1/Social-Gadget.xml>`__ A standard that defines how to write gadgets and provide APIs. Gadgets are particularly useful for integrating external applications into eXo Platform.
Web Services
`CMIS: <#eXoAddonsGuide.CMIS>`__ A standard API that gives access to the content repository via REST and SOAP Web services.
`FTP: <../../../reference/html/JCR.FTP.html>`__ A standard protocol for exchanging documents.
`OpenSocial 2.5.1 REST Protocol: <http://opensocial.github.io/spec/2.5.1/Social-API-Server.xml>`__ A standard API for accessing the social graph and activity streams.
`WebDAV: <../../../reference/html/JCR.WebDAV.html>`__ A standard protocol for exchanging document over HTTP.
Provisional API¶
Java API
UI Extensions: An API to plug new UI component to eXo applications.
UI Extension in Social
Working with eXo REST APIs¶
New REST APIs introduced in Platform 4.x
This part of the Developer guide introduced two REST APIs. These “v1” APIs are at Experimental level.
/v1/calendar
allows developers to work with eXo Calendar, is available as of 4.2./v1/social
allows developers to work with eXo Social, in other words, with user profile, connection, activity and space objects. It is new in 4.4.
Base URL and Rest context
Here is an example of the full URL: http://localhost:8080/rest/private/v1/social/users.
You should use the URL
/rest/private
to access the APIs.
While you can also use /portal/rest
, notice the /rest
context is
designed for secure accesses to the Rest services, so it is recommended.
You can omit /private
for an anonymous access, however almost all
methods of the two APIs require authentication.
Note
The paths are case-sensitive. For example, use
/v1/social/usersRelationships
, don’t use/v1/social/usersrelationships
.
Useful resources and tools
If you want to start from learning concepts of REST framework, you should read the Java (Oracle) documentation:
If you want to test the two APIs simply, a good way is using a web browser with plugins for Rest and JSON. For example with Chrome, you can install Advanced Rest Client and JSON Formatter extensions.
The following tutorials help you develop your eXo REST client in Java and JavaScript. The sample projects can be found at GitHub.
Authentication¶
Almost all the Rest operations (methods) require authentication. An authenticated user is used to check the access permission to the requested resource, but not only for that. For example, a POST to /rest/private/v1/social/spaces will create a space and set the authenticated user as the space manager.
At server side, within the method’s code, the authenticated user Id is
typically got via ConversationState
:
String currentUserId = ConversationState.getCurrent().getIdentity().getUserId();
From the client, you can use Basic Authentication to have a user authenticated.
If the user is requesting a resource via your gadget, or just by hitting
the URL in a browser, using /rest/private
is the way to make sure
he/she gets prompted to enter username and password, rather than getting
an Unauthorized error.
Via a tool like curl, you can use -u option:
curl -X GET -uroot:password http://localhost:8080/rest/private/v1/calendar
.
If you are developing a Java application using URLConnection, basically you have to add “Authorization” header to every request:
URL url = new URL("http://localhost:8080/rest/private/v1/calendar/calendars");
String auth = new sun.misc.BASE64Encoder().encode("root:password".getBytes());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Authorization", "Basic " + auth);
connection.connect();
In the Java examples of these tutorials, the application is a standalone that serves a single user at once. In this case, the procedure above can be automatically done by setting a default Authenticator:
import java.net.Authenticator;
import java.net.PasswordAuthentication;
//...
@SuppressWarnings("restriction")
public static void login(String username, String password) {
final String username_ = username;
final String password_ = password;
Authenticator.setDefault(new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username_, password_.toCharArray());
}
});
}
Notice with this method, the first signed username is cached (during the
live time of the JVM) and will not be unset by calling login() with
another user. So if you want to switch to another user, you need to
clear the AuthCache
first:
sun.net.www.protocol.http.AuthCacheValue.setAuthCache(new sun.net.www.protocol.http.AuthCacheImpl());
Calendar Rest API¶
Note
The /v1/calendar Rest API, available as of Platform 4.2, is considered Experimental. It is the new substitute of the deprecated /cs/calendar Rest API.
The Calendar Rest API meets the common requirements of new eXo Platform Rest services. Here are some tips that help you quickly get familiar with the service:
CRUD operations: There are four verbs used for CRUD operations: GET for Read, POST for Create, PUT for Update, and DELETE.
All methods consistently use JSON to transfer data in requests/reponses.
Query limit: A GET method can query a single item or a collection. In case of collection, there is a query limit on the server side that limits the number of items returned. This is configurable for effective performance.
To get the entire collection or implement some kinds of paging in your client, you rely on the three parameters:
returnSize
,limit
andoffset
.Queries for Events, Tasks and Occurrences always have finite time parameters. By default the time range is one week from the current server time.
In the next tutorials, you will learn the access permission and Java/JavaScript code samples to use the API:
Java client samples¶
The source of this sample can be found at eXo Docs-samples Repository.
The project implements an eXo Calendar Rest Connector that supports some
main CRUD operations for demo, that you can read over the
ExoCalendarConnectorInterface.java
:
public interface ExoCalendarConnectorInterface {
// Calendar : GET, GET, POST, PUT, DELETE.
public ExoCalendarCollection getCalendars() throws Exception;
public ExoCalendar getCalendarById(String calendar_id) throws Exception;
public String createCalendar(ExoCalendar calendar) throws Exception;
public int updateCalendar(ExoCalendar calendar, String calendar_id) throws Exception;
public int deleteCalendar(String calendar_id) throws Exception;
// Event : GET, GET, POST, PUT, DELETE.
public ExoEventCollection getEventsByCalendarId(String calendar_id) throws Exception;
public ExoEvent getEventById(String event_id) throws Exception;
public String createEvent(ExoEvent event, String calendar_id) throws Exception;
public int updateEvent(ExoEvent event, String event_id) throws Exception;
public int deleteEvent(String event_id) throws Exception;
// Task : GET, GET, POST, PUT, DELETE.
public ExoTaskCollection getTasksByCalendarId(String calendar_id) throws Exception;
public ExoTask getTaskById(String task_id) throws Exception;
public String createTask(ExoTask task, String calendar_id) throws Exception;
public int updateTask(ExoTask task, String task_id) throws Exception;
public int deleteTask(String task_id) throws Exception;
// Attachment (of event) : GET, GET, POST, DELETE.
public AttachmentCollection getAttachmentsByEventId(String event_id) throws Exception;
public Attachment getAttachmentById(String attachment_id) throws Exception;
public String createAttachment(List<Path> paths, String event_id) throws Exception;
public int deleteAttachment(String event_id) throws Exception;
// Invitation : GET, GET, POST, PUT, DELETE.
public InvitationCollection getInvitationsByEventId(String event_id) throws Exception;
public Invitation getInvitationById(String invitation_id) throws Exception;
public String createInvitation(Invitation invitation, String event_id) throws Exception;
public int updateInvitation(Invitation invitation, String invitation_id) throws Exception;
public int deleteInvitation(String invitation_id) throws Exception;
}
The overview of the project:
ExoCalendarConnector.java
(the Connector) implements the above functions. It also provides a static Gson object.org.exoplatform.calendar.client.model.*
provides POJO classes that represent JSON request/response data. The models will be used by Gson that helps you parse JSON data effectively.org.exoplatform.calendar.client.rest.connector.HttpUtils
provides Http Verbs, so the Connector does not need to care about setting up a Http client and sending requests.
JSON Parsing
You can use Gson, Jackson or alternate libraries to parse JSON to Java object and vice versa.
Gson is used in this sample. Its dependency is:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>
You should write POJO classes for every object you want to work with.
See the classes in org.exoplatform.calendar.client.model
package.
The following is the ExoCalendar POJO:
package org.exoplatform.calendar.client.model;
public class ExoCalendar {
String editPermission;
String viewPermission;
String privateURL;
String publicURL;
String icsURL;
String color;
String name;
String type;
String owner;
String timeZone;
String description;
String[] groups;
String href;
String id;
// Getters and setters.
// ...
}
To serialize the object to a JSON string, or deserialize:
Gson gson = new Gson();
// serialize object to JSON
String json = gson.toJson(calendar_object);
// parse JSON to an object
ExoCalendar new_calendar_object = gson.fromJson(json, ExoCalendar.class);
The JSON string that is returned from a single calendar query:
{
"editPermission": "",
"viewPermission": "",
"privateURL": null,
"publicURL": null,
"icsURL": "http://localhost:8080/rest/private/v1/calendar/calendars/john-defaultCalendarId/ics",
"description": null,
"color": "asparagus",
"timeZone": "Europe/Brussels",
"groups": null,
"name": "John Smith",
"type": "0",
"owner": "john",
"href": "http://localhost:8080/rest/private/v1/calendar/calendars/john-defaultCalendarId",
"id": "john-defaultCalendarId"
}
A collection query JSON always looks like this:
{
"limit": 10,
"data": [
{calendar1},
{calendar2}
],
"size": -1,
"offset": 0
}
The POJO for collection:
public class ExoCalendarCollection {
int limit;
int size;
int offset;
ExoCalendar[] data;
// Getters and setters.
// ...
}
ISO8601 DateTime format
Some key fields require a correct DateFormat to parse/format:
To query events/tasks of a certain time, you need to send a couple of start and end time strings in ISO8601 format.
The fields “from” and “to” in Event/Task JSON are ISO8601 too.
In Java 7, you can use SimpleDateFormat
with the following pattern
to parse/format those fields:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date date1 = new Date();
//format
String s = df.format(date1);
//parse
Date date2 = df.parse(s);
However, the pattern may not work in other Platforms. Another approach is to re-use eXo’s ISO8601 class. It is potentially helpful when you need to parse ‘Z’ timezone.
The source is here. You can use the following Maven dependency:
<dependency>
<groupId>org.exoplatform.kernel</groupId>
<artifactId>exo.kernel.commons</artifactId>
</dependency>
The code sample of using this util:
import org.exoplatform.commons.utils.ISO8601;
//
String s1 = "2015-01-15T05:00:000Z";
Calendar cal = ISO8601.parse(s1);
System.out.println(cal.getTime());
String s2 = ISO8601.format(cal);
System.out.println(s2);
Using HttpURLConnection for CRUD requests
You need a Http Client to send requests to the Rest service. In the
sample, java.net.HttpURLConnection
is used.
Here is the code for sending a GET. The result string then can be converted to a Calendar or Event or some object accordingly.
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
// GET
public static String get(String url) throws Exception {
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
connection.setRequestMethod("GET");
connection.connect();
int code = connection.getResponseCode();
if (code > 300) {
connection.disconnect();
return null;
}
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
in.close();
reader.close();
connection.disconnect();
return builder.toString();
}
For a POST request, pay attention to set the request method and the content-type:
import java.io.DataOutputStream;
// POST
public static String post(String json, String url) throws Exception {
HttpURLConnection connection = (HttpURLConnection) (new URL(url)).openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", "application/JSON");
// Write to the connection output stream.
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes(json);
int code = connection.getResponseCode();
if (code > 300) {
out.flush();
out.close();
connection.disconnect();
return null;
}
String href = connection.getHeaderField("Location");
out.flush();
out.close();
connection.disconnect();
return href;
}
Here you see value of the (response) header “Location” is returned. All the Create operations should return this on success. For example when you create an Event, the event’s href (a URL to continue to get the created event) is returned.
See PUT and DELETE code in HttpUtils.java
.
CRUD Examples
With the POJO models, CRUD operations are very similar between kinds of objects. Hereunder is a code sample to do a chain of tasks:
- create a calendar
- create an event
- update the event
- delete the event
- delete the calendar
ExoCalendarConnector connector = new ExoCalendarConnector("http://localhost:8080");
String created = Long.toString(System.currentTimeMillis());
// Create calendar.
ExoCalendar calendar = new ExoCalendar();
calendar.setType("0");
calendar.setName(created);
connector.createCalendar(calendar);
// Get the list of calendars and search for one.
String calendar_id = null;
ExoCalendar[] calendars = connector.getCalendars().getData();
int len = calendars.length;
for (int i = 0; i < len; i++) {
if (calendars[i].getName().equals(created)) {
calendar_id = calendars[i].getId();
}
}
// Create event.
ExoEvent event = new ExoEvent();
event.setSubject(created);
Date from = new Date((new Date()).getTime() + TimeUnit.DAYS.toMillis(1)); //from = tomorrow
Date to = new Date(from.getTime() + TimeUnit.HOURS.toMillis(4)); //to = from + 4 hours
event.setFrom((new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(from));
event.setTo((new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(to));
String href = connector.createEvent(event, calendar_id);
System.out.println("Event created, href : " + href);
// Get the list of events then get an event specified by id.
ExoEvent[] events = connector.getEventsByCalendarId(calendar_id).getData();
len = 0; len = events.length; String event_id = null;
for (int i = 0; i < len; i++) {
if (events[i].getSubject().equals(created)) {
event_id = events[i].getId();
}
}
ExoEvent new_event = connector.getEventById(event_id);
System.out.println("Event found, its from is : " + new_event.getFrom());
// Update the event.
new_event.setDescription(created);
System.out.println("Update event, response code : " + connector.updateEvent(new_event, event_id));
// Delete the event.
System.out.println("Delete event, response code : " + connector.deleteEvent(event_id));
// Delete the calendar.
System.out.println("Delete calendar, response code : " + connector.deleteCalendar(calendar_id));
Uploading attachment files
eXo Rest framework uses Apache upload service, then you need to send files in multipart/form-data in order to create attachments. The following code shows how to send a POST with multipart content. The method accepts a list of java.nio.file.Path, for each Path the file data is written to a content part with boundary.
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.io.DataOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
// UPLOAD
public static String upload(List<Path> paths, String url) throws Exception {
// form-data stuffs
String crlf = "\r\n";
String twoHyphens = "--";
String boundary = "*****";
String attachmentName;
String attachmentFileName;
byte[] data;
// set up the connection
HttpURLConnection connection = (HttpURLConnection) (new URL(url).openConnection());
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Cache-Control", "nocache");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
// write to connection output stream
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
int len = paths.size();
for (int i = 0; i < len; i++) {
attachmentFileName = paths.get(i).getFileName().toString();
attachmentName = attachmentFileName;
data = Files.readAllBytes(paths.get(i));
out.writeBytes(twoHyphens + boundary + crlf);
out.writeBytes("Content-Disposition: form-data;"
+ "name=\"" + attachmentName + "\";"
+ "filename=\"" + attachmentFileName + "\"" + crlf);
out.writeBytes(crlf);
out.write(data);
out.writeBytes(crlf);
}
out.writeBytes(twoHyphens + boundary + twoHyphens + crlf);
int code = connection.getResponseCode();
if (code > 300) {
out.flush();
out.close();
connection.disconnect();
return null;
}
String href = connection.getHeaderField("Location");
out.flush();
out.close();
connection.disconnect();
return href;
}
JavaScript client samples¶
eXo Platform provides a great advance of using REST APIs so that you can use any web development language to access them. In this section, standard Ajax GET, PUT, DELETE and POST methods are used. This part instructs you how to leverage these APIs to work with Calendar objects, including:
Others (attachments, occurrences, invitations, categories and feeds) <PLFDevGuide.eXoPlatformAPIs.RestAPIs.Calendar.JavaScript.Others>
See here for more details of which Calendar APIs are provided. Note that you can download all the source code used in this section here.
Refer to thisguide to create a gadget with the following structure:
Follow instructions there to set suitable configurations, except the
main body (CalendarHTTPRequestGadget.xml
) of the gadget which will
be replaced in each part below.
Calendars¶
eXo Platform provides APIs to create, update, delete and get information of a calendar.
Getting information of a calendar
Replace content of the
CalendarHTTPRequestGadget.xml
file with this script:<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Calendar HTTP Request Gadget"> <Require feature="opensocial-0.8" /> </ModulePrefs> <Content type="html"> <![CDATA[ <!--Including platform css--> <link href="/eXoSkin/skin/css/Core.css" rel="stylesheet"></link> <link href="/eXoSkin/skin/css/sub-core.css" rel="stylesheet"></link> <!--Including JQuery library--> <script src="jquery-3.2.1.js"></script> <!--Gadget's main body which will be added by HTML DOM Object later--> <div id="calendar-show" style="max-width: 800px" class="uiComposer uiGrayLightBox"> <div class="input-append" style="margin: 20px 30px 20px 30px"> //control button for getting default calendar of the current logged-in user <button id="get_default_cal_btn" onclick="getDefaultCalendar()" class="btn btn-primary" >Get default calendar</button> //control button for getting a calendar by id <button id="get_calid_btn" onclick="getCalendarById()" class="btn btn-primary">Get calendar by id</button><input type="text" id="calid_txt" placeholder="Enter calendar id..."> </div> </div> <!--Start calling js function--> <script type="text/javascript"> //get the current logged-in user via OpenSocial var current_user; loadViewer(); function loadViewer() { var req = opensocial.newDataRequest(); req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), 'viewer'); req.send(onLoadViewer); } function onLoadViewer(data) { var viewer = data.get('viewer').getData(); //get current user current_user = viewer.getId(); }]]> </Content> </Module>
Deploy this gadget. You will see what is going next.
This UI offers 2 functions which are: getting the default calendar or retrieving other calendars by entering a specified Id.
Implement the
getDefaultCalendar()
function by adding the following source code to the Javascript paragraph of the gadget:function getDefaultCalendar(){ //get default calendar of the current logged-in user //This function uses AJAX to send GET request to //Calendar REST API GET /rest/private/v1/calendar/calendars/ $.ajax({ type: "GET", url: "/rest/private/v1/calendar/calendars/"+current_user+"-defaultCalendarId", success: function (data, status, jqXHR) { processGetCalendar(data); }, error: function (jqXHR, status) { alert("Cannot retrieve data!"); } }); }
The above function uses an Ajax GET request. It calls the REST API /rest/private/v1/calendar/calendars/ and passes *-defaultCalendarId which is the default calendar’s Id of the current logged-in user. If successful, the result will then be passed through the
processGetCalendar()
function. This function will be implemented later.Consider how to get and pass the entered calendar Id to this REST API instead of hard-coding it by adding the source code:
function getCalendarById(){ //get the entered calendar id var cal_id=$('#calid_txt').val(); //This function uses AJAX to send GET request to //Calendar REST API GET /rest/private/v1/calendar/calendars/ $.ajax({ type: "GET", url: "/rest/private/v1/calendar/calendars/"+cal_id, success: function (data, status, jqXHR) { //print the result processGetCalendar(data); }, error: function (jqXHR, status) { alert("Cannot retrieve data!"); } }); }
This function uses JQuery to get value of the text box for entering calendar Id and pass it to
url
parameter of this call. Now, it’s time to process returned results!The data returned from a Calendar GET request is under a JSON object. Therefore, you could implement the
processGetCalendar()
as below:function processGetCalendar(results){ //clean the old screen $("#calendar-result").remove(); //add calendar object's information var obj_content="<div id=\"calendar-result\"><table class=\"uiGrid table table-hover table-striped\"><thead><tr><th>Properties</th><th>Value</th></tr></thead><tbody>"; //loop through the calendar object's properties $.each(results, function(key, value) { obj_content+="<tr><td>"+key+"</td><td>"+value+"</td></tr>"; }); //add closing tag obj_content+="<tr><td><b>Execute functions</b></td><td><button id=\"submit_cal_btn\" onclick=\"updateCalendar()\">Update</button> <button id=\"delete_cal_btn\" onclick=\"deleteCalendar()\">Delete</button></td></tr></tbody></table></div>"; //print to screen $('#calendar-show').append(obj_content); }
Deploy and test the gadget to see what has been done by clicking Get default calendar. You will see the default calendar’s information displayed:
To test the Get calendar by id function, move to the next part of creating a new calendar.
Creating a new calendar
Add a control button in front of others like this:
... //control button for creating a new calendar <button id="create_cal_btn" onclick="createCalendar()" class="btn btn-primary">Create a new calendar</button> //control button for getting default calendar of the current logged-in user <button id="get_default_cal_btn" onclick="getDefaultCalendar()" class="btn btn-primary" >Get default calendar</button> //control button for getting a calendar by id <button id="get_calid_btn" onclick="getCalendarById()" class="btn btn-primary">Get calendar by id</button><input type="text" id="calid_txt" placeholder="Enter calendar id..."> ...
Implement the
createCalendar()
function as below:function createCalendar(){ //clean the old screen $("#calendar-result").remove(); //add a simple calendar input form $('#calendar-show').append("<div id=\"calendar-result\"><table class=\"uiGrid table table-hover table-striped\">"+ "<thead><tr><th>Properties</th><th>Value</th></tr></thead><tbody>"+ "<tr><td>Name</td><td><input type=\"text\" id=\"cal-name\"></td></tr>" +"<tr><td>Type</td><td><select id=\"cal-type\"><option value=\"0\">Personal calendar</option></select></td></tr>" +"<tr><td>Description</td><td><textarea id=\"cal-description\" rows=\"4\" cols=\"50\"></textarea></td></tr>" +"<tr><td>Color</td><td><select id=\"cal-color\"><option value=\"red\">Red</option><option value=\"yellow\">Yellow</option></select></td></tr>" +"<tr><td><b>Execute functions</b></td><td><button id=\"create_cal_btn\" onclick=\"submitCalendar()\">Submit</button></td></tr>" +"</tbody></table></div>"); }
This UI allows to create a simple calendar with properties, such as name, type, description and color.
Create an Ajax POST request to call this API by implementing the
submitCalendar()
as follows:function submitCalendar(){ //get entered cal_json_obj var cal_json_obj={ "name":$("#cal-name").val(), "owner": current_user, "type":$("#cal-type").val(), "description":$("#cal-description").val(), "color":$("#cal-color").val() }; $.ajax({ url: "/rest/private/v1/calendar/calendars", contentType: "application/json", data: JSON.stringify(cal_json_obj), method: "POST" }).done(function (data, status, xhr) { //clean the old screen $("#calendar-result").remove(); //get the newly created calendar id var locat=xhr.getResponseHeader('Location'); var new_cal = locat.split("calendar/calendars/"); $('#calendar-show').append("<div id=\"calendar-result\"><i>Created successfully! Calendar id: "+new_cal[1]+"</i></div>"); }).fail(function (jqxhr, textStatus, error) { alert("fail"); }); }
This script passes the user input as a JSON object to the POST request. If successful, the returned result will be the newly created calendar Id.
Deploy this gadget to see the input form for creating a new calendar, and fill in this form like:
Click Submit button to execute this function and see the message informing that you have successfully created a new calendar. For example: “Created successfully! Calendar id: calendard94b0488c0a8043a7f3b0da4a6318abf“.
To check the new calendar, copy the attached Id in this message and paste it to the input text of the Get calendar by id function. You will see its information displayed.
Updating a calendar
It is assumed that you need to update properties of a calendar, for instance its name and description. To avoid passing local variables too many times, you will use a global one called “current_cal_obj” that points to the processing calendar object.
Add a declaration of this variable to the Javascript paragraph of the gadget:
var current_cal_obj;
Now, each time you query a calendar, this variable should be updated by adding the script:
current_cal_obj=results;
to the processGetCalendar() function in getting information of a calendar.
Implement the
updateCalendar()
function as follows:function updateCalendar(){ //clean the old result $("#calendar-result").remove(); //add new calendar object's information var obj_content="<div id=\"calendar-result\"><table class=\"uiGrid table table-hover table-striped\"><thead><tr><th>Properties</th><th>Value</th></tr></thead><tbody>"; //loop through the new calendar object's properties $.each(current_cal_obj, function(key, value) { if((key=="name")||(key=="description")){ obj_content+="<tr><td>"+key+"</td><td><textarea id=\"cal-"+key+"\">"+value+"</textarea></td></tr>"; } else { obj_content+="<tr><td>"+key+"</td><td>"+value+"</td></tr>"; } }); //add closing tag obj_content+="<tr><td><b>Execute functions</b></td><td><button id=\"submit_cal_btn\" onclick=\"submitFullCalendar()\">Submit</button> <button id=\"delete_cal_btn\" onclick=\"deleteCalendar()\">Delete</button></td></tr></tbody></table></div>"; //add to screen $('#calendar-show').append(obj_content); }
As seen above, you can easily manage the information of the processing calendar object through the global variable.
Deploy the gadget and select a specific calendar, for instance the default one. Then, click the Update button, you will see the name and description fields enabled for editing mode:
If you click the Submit button, nothing happens since you have not implemented the submitFullCalendar() function. To make an update query, you need to send an Ajax PUT request as follows:
function submitFullCalendar(){
//get changed properties
current_cal_obj.name=$("#cal-name").val();
current_cal_obj.description=$("#cal-description").val();
$.ajax({
url: "/rest/private/v1/calendar/calendars/"+current_cal_obj.id,
contentType: "application/json",
data: JSON.stringify(current_cal_obj),
method: "PUT"
}).done(function (data, status, xhr) {
$("#calendar-result").remove();
$('#calendar-show').append("<div id=\"calendar-result\"><i>Updated successfully!</i></div>");
}).fail(function (jqxhr, textStatus, error) {
alert("fail");
});
}
Add this function to the Javascript paragraph of the gadget and redeploy it. Now, test your update function and see how it works!
Deleting a calendar
To delete a calendar, you need to make an Ajax DELETE request by implementing the deleteCalendar() function as follows:
function deleteCalendar(){
$.ajax({
url: "/rest/private/v1/calendar/calendars/"+current_cal_obj.id,
method: "DELETE"
}).done(function () {
$("#calendar-result").remove();
$('#calendar-show').append("<div id=\"calendar-result\"><i>Deleted successfully!</i></div>");
}).fail(function (jqxhr, textStatus, error) {
alert("fail");
});
}
This function gets the processing calendar Id from the global variable “current_cal_obj” for passing to the url parameter. And now, deploy this gadget to test this function.
Events¶
eXo Platform provides APIs to create, update, delete and get information of an event. An event is represented as a JSON object as follows:
{
//Where the event is organized
"location": "",
//Priority of the event
"priority": "",
//Detailed description of the event
"description": "",
//Title of the event
"subject": "",
//Which calendar the event belongs to
"calendar": "",
//Status of the event, for example "busy"
"availability": "",
//All attachments of the event
"attachments": [ ],
//Recurrence id of the event
"recurrenceId": "",
//Id of the category which the event belongs to
"categoryId": "",
//Repetition information of the event
"repeat": {},
//Settings for reminding the event
"reminder": [],
//Privacy of the event
"privacy": "",
//Starting time of the event
"from": "",
//Link to the category the event belongs to
"categories": [],
//Ending time of the event
"to": "",
//Original event if any
"originalEvent": "",
//All participants of the event
"participants": [],
//Id of the event
id": "",
//Link to the event
"href": ""
}
Getting information of an event
To retrieve information of an event, you need to send an Ajax GET request to /rest/private/v1/calendar/events/ by the following script:
function getEventById(){
$.ajax({
type: "GET",
url: "/rest/private/v1/calendar/events/"+event_id,
success: function (data, status, jqXHR) {
//process the returned data here
},
error: function (jqXHR, status) {
alert("Cannot retrieve data!");
}
});
}
In which, event_id is Id of the event that you want to retrieve information. This request returns the information under a JSON object as indicated above.
Creating a new event
Declare a simple event object such as:
var new_event={
"description": "Welcoming new comers in February",
"subject": "Welcome event",
"categoryId": "defaultEventCategoryIdMeeting",
"privacy": "private",
"from": "2015-07-30T04:30:00.000Z",
"to": "2015-07-30T05:30:00.000Z"
}
Use an Ajax POST request to send this object to /rest/private/v1/calendar/calendars/{calendarId}/events as the following script:
function createNewEvent(){
$.ajax({
url: "/rest/private/v1/calendar/calendars/"+current_user+"-defaultCalendarId"+"/events",
contentType: "application/json",
data: JSON.stringify(new_event),
method: "POST"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("failed");
});
}
In which, the new event will be added to the default calendar of the current logged-in user.
Updating an event
To update an event, you need to use this PUT request:
function updateEvent(){
$.ajax({
url: "/rest/private/v1/calendar/events/"+event_id,
contentType: "application/json",
data: JSON.stringify(new_content),
method: "PUT"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("failed");
});
}
In which, event_id is Id of the event that you want to update, and new_content is a JSON object containing new content of the event, for example:
var new_content={
"description": "Welcoming new comers in March",
"subject": "Monthly welcome event",
"categoryId": "defaultEventCategoryIdMeeting",
"privacy": "public",
"from": "2015-07-30T04:30:00.000Z",
"to": "2015-07-30T05:30:00.000Z"
}
Deleting an event
To delete an event, use the following DELETE request:
function deleteEvent(){
$.ajax({
url: "/rest/private/v1/calendar/events/"+event_id,
method: "DELETE"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("fail");
});
}
In which, event_id is Id of the event that you want to delete.
Tasks¶
eXo Platform provides APIs to create, update, delete and get information of a task. A task is represented as a JSON object as follows:
{
//Name of the task
"name": "",
//Priority of the task
"priority": "",
//Status of the task
"status": "",
//Which calendar the task belongs to
"calendar": "",
//Detailed note of the task
"note": "",
//Settings for reminding the task
"reminder": [],
//Which users are assgined
"delegation": [],
//Starting time
"from": "",
//Id of the category which the task belongs to
"categoryId": "",
//Ending time
"to": "",
//All attachments of the task
"attachments": [ ],
//Link to the category which the task belongs to
"categories": [],
//Id of the task
"id": "",
//Link to the task
"href": ""
}
Getting information of a task
To retrieve information of a task, you can use a GET request as below:
function getTaskById(){
$.ajax({
type: "GET",
url: "/rest/private/v1/calendar/tasks/"+task_id,
success: function (data, status, jqXHR) {
//process the returned data here
},
error: function (jqXHR, status) {
alert("Cannot retrieve data!");
}
});
}
In which task_id is Id of the task that you want to get information. This request returns the result under a JSON object as indicated above.
Creating a new task
Declare a simple task object such as:
var new_task={
"name": "Documentation blog",
"note": "Writing a documentation blog for the next deployment",
"from": "2015-07-30T04:30:00.000Z",
"to": "2015-07-30T05:30:00.000Z",
"delegation": [current_user, "john"]
}
where this task will be assigned to current_user and john. Use an Ajax POST request to send this object to /rest/private/v1/calendar/calendars/{calendarId}/tasks as the following script:
function createNewEvent(){
$.ajax({
url: "/rest/private/v1/calendar/calendars/"+current_user+"-defaultCalendarId"+"/events",
contentType: "application/json",
data: JSON.stringify(new_event),
method: "POST"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("failed");
});
}
In which, the new task will be added to the default calendar of the current logged-in user.
Updating a task
To update a task, you need to use this PUT request:
function updateTask(){
$.ajax({
url: "/rest/private/v1/calendar/tasks/"+task_id,
contentType: "application/json",
data: JSON.stringify(new_content),
method: "PUT"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("fail");
});
}
In which, task_id is Id of the task that you want to update and new_content is a JSON object containing new content of the task, for example:
var new_content={
"name": "Writing documentation blog",
"note": "Writing a documentation blog for the next deployment in June",
"from": "2015-05-30T04:30:00.000Z",
"to": "2015-05-30T05:30:00.000Z",
"delegation": ["john"]
}
Deleting a task
To delete a task, use the following DELETE request:
function deleteTask(){
$.ajax({
url: "/rest/private/v1/calendar/tasks/"+task_id,
method: "DELETE"
}).done(function (data, status, xhr) {
//process the returned data here
}).fail(function (jqxhr, textStatus, error) {
alert("fail");
});
}
In which, task_id is Id of the task that you want to delete.
Others¶
To deal with the other objects of Calendar (attachments, occurrences, invitations, categories and feeds), eXo Platform provides full APIs for GET, POST, PUT and DELETE requests:
GET /v1/calendar/events/{id}/attachments: gets all attachments of an event with a specified Id.
GET /v1/calendar/attachments/{id}: gets an attachment with a specified Id.
POST /v1/calendar/events/{id}/attachments: creates an attachment for an event with a specified Id.
DELETE /v1/calendar/attachments/{id}: deletes an attachment with a specified Id.
GET /v1/calendar/events/{id}/occurrences: gets all occurrences of a recurring event with a specified Id.
GET /v1/calendar/categories: gets all categories of the current logged-in user.
GET /v1/calendar/categories/{id}: gets a category with a specified Id.
GET /v1/calendar/feeds/{id}: gets a feed with a specified Id.
PUT /v1/calendar/feeds/{id}: updates a feed with a specified Id.
DELETE /v1/calendar/feeds/{id}: deletes a feed with a specified Id.
GET /v1/calendar/feeds/{id}/rss: gets RSS stream of a feed with a specified Id.
GET /v1/calendar/invitations/{id}: gets an invitation with a specified Id.
PUT /v1/calendar/invitations/{id}: updates an invitation with a specified Id.
DELETE /v1/calendar/invitations/{id}: deletes an invitation with a specified Id.
GET /v1/calendar/invitations/{id}: gets all invitations of an event with a specified Id.
POST /v1/calendar/events/{id}/invitations/: creates an invitation for an event with a specified Id.
To call these APIs, simply follow the examples of event, task and calendar objects. In this section, you will know how to work with sending multipart/form-data via the POST /v1/calendar/events/{id}/attachments API.
Replace content of the
CalendarHTTPRequestGadget.xml
file with this script:<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Calendar HTTP Request Gadget"> <Require feature="opensocial-0.8" /> </ModulePrefs> <Content type="html"> <![CDATA[ <!--Including platform css--> <link href="/eXoSkin/skin/css/Core.css" rel="stylesheet"></link> <link href="/eXoSkin/skin/css/sub-core.css" rel="stylesheet"></link> <!--Including JQuery library--> <script src="jquery-3.2.1.js"></script> <!--Gadget's main body which will be added by HTML DOM Object later--> <div id="calendar-show" style="max-width: 800px" class="uiComposer uiGrayLightBox"> <div class="input-append" style="margin: 20px 30px 20px 30px"> <form id="your_form_id" class="form-horizontal"> <fieldset> <!-- Form name --> <legend>Add attachment</legend> <!-- Text input--> <label>Event: </label><input type="text" id="eventid_txt" placeholder="Enter event id..."><br/><br/> <!-- File button --> <label>Attachment: </label><input id="file_attachment" name="pdf" class="input-file" type="file"><br/><br/> <!-- Submit button --> <button id="submit" name="submit" class="btn btn-primary" align="center">Submit</button> </fieldset> </form> </div> </div> <!--Start calling js function--> <script type="text/javascript"> $(document).ready(function() { $('#your_form_id').submit(function(e) { e.preventDefault(); var form_data = new FormData($(this)[0]); $.ajax({ url : '/rest/private/v1/calendar/events/'+$('#eventid_txt').val()+'/attachments', type : 'POST', contentType : false, data : form_data, processData : false, success : function(data) { // Handle the response on success } }); }); }); </script> ]]> </Content> </Module>
This script creates a form which allows you to input 2 data fields. The first one is Id of the event that you want to add an attachment, while the second one is the attachment of this event.
Deploy this gadget, you will see the following UI:
In the POST request, the input event Id is passed to the url parameter while the attached file is put into a FormData object.
Enter an available event Id and browse to any local file that you want to attach, for example a
.doc
file.Click Submit, then check attachments of the event. You will see the
.doc
file is created.
Service configuration¶
Hereunder is the default configuration of the Calendar Rest service:
<component>
<type>org.exoplatform.calendar.ws.CalendarRestApi</type>
<init-params>
<value-param>
<name>query_limit</name>
<value>10</value>
</value-param>
</init-params>
</component>
The parameter query_limit
is used to limit the number of items
returned by a query. This applies on the methods that have query param
limit
. If limit is absent or it exceeds query_limit, then
query_limit will be the maximum number of items a query can return.
For example, this query GET /v1/calendar/calendars?limit=20
sends an
exceeding limit, so only 10 or fewer items will be returned.
You can use a portal extension
to override the service configuration. In your custom-extension.war!/WEB-INF/conf/configuration.xml
write this configuration:
<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
<component>
<type>org.exoplatform.calendar.ws.CalendarRestApi</type>
<init-params>
<value-param>
<name>query_limit</name>
<value>${exo.calendar.rest.query_limit:100}</value>
</value-param>
</init-params>
</component>
</configuration>
This changes query_limit to 100, or to the value of exo.calendar.rest.query_limit property if that property is set in exo.properties file.
Portal API¶
The Portal API provides access to retrieving, creating and deleting Portal Sites and Portal Pages. It also provides full support for the navigation and nodes associated with a site, which can be very useful for example creating custom navigation portlets. This section only gives some basic examples of the API usage. For full documentation of all features available, please refer to the Javadoc.
PortalRequest and Portal
The PortalRequest is the entry point to the API. It provides access to the Portal, and it also provides access to information associated with the current request (such as the logged in user and the page being requested).
The example below shows how to use the PortalRequest to obtain an instance of the Portal:
Portal portal = PortalRequest.getInstance().getPortal();
This sections contains these parts:
Site¶
A Site is comprised of a set of pages, navigation definitions, and can be assigned a unique skin. There are three different types of sites: standard sites, group spaces and user dashboards.
Retrieving Sites
A specific site can be retrieved by its SiteId
. The SiteId
is
constructed by passing either the name for a standard site, the group
for a group space or the user for a user dashboard. Alternatively it can
be constructed by passing the SiteType and name.
Example of Retrieving Sites is Shown below:
Site standardSite = portal.getSite(new SiteId("classic"));
Site groupSpace = portal.getSite(new SiteId(new Group("platform", "administrators")));
Site userDashboard = portal.getSite(new SiteId(new User("john")));
It is also possible to query for sites using the SiteQuery. The SiteQuery supports filtering, sorting and pagination when querying for sites.
The example below finds standard sites that a user has access permissions to, limited to the 10 first results:
SiteQuery.Builder qb = new SiteQuery.Builder();
qb.withSiteTypes(SiteType.SITE).withPagination(0, 10).withFilter(new Filter<Site>() {
public boolean accept(Site site) {
return portal.hasPermission(user, site.getAccessPermission());
}
});
List<Site> sites = portal.findSites(qb.build());
Note
The default number of results to return is 15. Which means that if no pagination is set while building the query, the maximum number of results will be 15. So be sure to set the correct pagination while querying.
Creating a Site
To create a site, first create a new site through the Portal, then set the configuration for the site (such as localized display names and skin), and finally save it using the Portal.
The example below creates a new standard site with the name mysite and a non-localized display name:
Site site = portal.createSite(new SiteId("foo"));
site.setDisplayName("My Site");
portal.saveSite(site);
Note
The site is not visible (or persisted) until saveSitesaveSite is invoked.
Setting Attributes for a Site
It is possible to change attributes for the site, supported attributes are:
sessionAlive : sets if a session is kept alive or not. Valid values are “onDemand”, “always”, and “never” with the default being “onDemand”.
showPortletInfo : sets if the info bar is shown by default when adding applications to pages, the default is ‘false’.
The attributes are set by using the associated Key, the example below changes the sessionAlive attribute:
site.getAttributes().put(Site.AttributeKeys.SESSION_BEHAVIOR, "always");
Setting Permission for a Site
Associated with a site is an access permission and an edit permission, which controls what users and groups are allowed to access and edit the site respectively.
The example below makes a site accessible by everyone, but only editable by users in the /platform/administrators group:
site.setAccessPermission(Permission.everyone());
site.setEditPermission(Permission.any("platform", "administrators"));
portal.saveSite(site);
Note
The changed permissions do not take affect until saveSite is invoked.
Deleting a site
A site is deleted by invoking removeSite on the Portal.
Page¶
Portal Page aggregates content from one or more Portlets.
Retrieving Pages
A specific page can be retrieved by its PageId. The PageId is constructed by passing either the name for a standard site, the group for a group space or the user for a user dashboard, and the name of the page. Alternatively it can be constructed by passing the SiteId and name of the page.
Example of retrieving specific pages is shown below:
Page standardSitePage = portal.getPage("classic", "home");
Page groupSpacePage = portal.getSite(new Group("platform", "administrators"), "grouppage");
Page userDashboardPage = portal.getSite(new User("john", "johnspage"));
It is also possible to query for pages using the PageQuery. The PageQuery supports filtering, sorting and pagination when querying for pages.
The example below finds pages with a display name that starts with “A”:
PageQuery.Builder qb = new PageQuery.Builder();
qb.withSiteType(SiteType.SITE).withFilter(new Filter<Page>() {
public boolean accept(Page page) {
return page.getDisplayName().startsWith("A");
}
});
List<Page> pages = portal.findPages(qb.build());
Creating a Page
To create a page, first create a new page through the Portal, then set the configuration for the page (such as localized display names), and finally saved it using the Portal.
The example below creates a new page in the classic site with the name mypage and a non-localized display name:
Page page = portal.createPage(new PageId("classic", "mypage"));
page.setDisplayName("My Page");
portal.savePage(page);
Note
The changed permissions do not take affect until savePage is invoked.
Setting Permissions for a Page
Associated with a page is an access permission and an edit permission, which controls what users and groups are allowed to access and edit the page respectively.
The example below makes a page accessible to everyone, but only editable by users in the /platform/administrators group:
page.setAccessPermission(Permission.everyone());
page.setEditPermission(Permission.any("platform", "administrators"));
portal.savePage(page);
Note
The changed permissions do not take affect until savePage is invoked.
Deleting a Page
A page is deleted by invoking removePage on the Portal.
OAuth Provider API¶
The interface OAuthProvider is a part of our public API. It is the entry point to perform operations on OAuth providers (social networks).
Please refer to OAuth section for details about the configuration of eXo Platform which is necessary to use OAuth providers (Facebook, Google, Twitter) for authentication of users. Once a user is logged in (or his account is linked with OAuth provider), his access token is saved in eXo Platform IDM database as a part of his User Profile. Then it is possible to retrieve his OAuth access token via OAuthProvider interface and run its operations. It is also possible to revoke or validate existing access tokens or send request to obtain new access tokens with more scopes (privileges).
Tip
Except for the next two sections, where we present some basic use of the the OAuthProvider API, there is also a standalone code example called Social Portlets.
Retrieve an Instance of OAuthProvider
First, you need to retrieve the appropriate instance of OAuthProvider from Portal:
Portal portal = PortalRequest.getInstance().getPortal();
OAuthProvider facebookProvider = portal.getOAuthProvider(OAuthProvider.FACEBOOK)
Currently eXo Platform supports three OAuth providers:
OAuthProvider.FACEBOOK for Facebook.
OAuthProvider.GOOGLE for Google+.
OAuthProvider.TWITTER for Twitter.
OAuthProvider Operations
The following snippet shows some basic use of OAuthProvider API:
// Retrieve instance of Google OAuth provider
OAuthProvider googleProvider = PortalRequest.getInstance().getPortal().getOAuthProvider(OAuthProvider.GOOGLE);
// Check if Google was enabled in configuration.properties
if (googleProvider == null) {
renderResp.getWriter().println("Authentication with Google not available. See OAuth section in Reference Guide for how to enable it");
return;
}
// Retrieve the key and display name of the social network
String key = googleProvider.getKey();
String friendlyName = googleProvider.getFriendlyName();
renderResp.getWriter().println(friendlyName + " is enabled");
// Retrieve access token of the current user
AccessToken accessToken = googleProvider.loadAccessToken(renderReq.getRemoteUser());
// Check if access token is available. It's the case when this user was registered/authenticated into portal
// through Google+ or if he linked his account with Google+
if (accessToken == null) {
renderResp.getWriter().println("Your account is not linked with Google+. You can link it in 'Social network' tab of " +
"user settings or you can authenticate through Google into portal");
return;
}
// Check if access token is valid and refresh it if necessary
try {
accessToken = googleProvider.validateTokenAndUpdateScopes(accessToken);
} catch (OAuthApiException oauthException) {
if (oauthException.getExceptionCode().equals(OAuthApiExceptionCode.ACCESS_TOKEN_ERROR)) {
renderResp.getWriter().println("Your access token is invalid or has been revoked");
} else if (oauthException.getExceptionCode().equals(OAuthApiExceptionCode.IO_ERROR)) {
renderResp.getWriter().println("Network error during the communication with Google");
}
}
// Check all available scopes
String availableScopes = accessToken.getAvailableScopes();
// Check if we have scope to call Google+ operations
if (!availableScopes.contains("https://www.googleapis.com/auth/plus.login")) {
// Redirect to Google+ and ask for plus.login scope
googleProvider.startOAuthWorkflow("https://www.googleapis.com/auth/plus.login");
return;
}
// Obtain Google API object to call Google plus API operations
Plus service = googleProvider.getAuthorizedSocialApiObject(accessToken, Plus.class);
// Retrieve activities from Google+ wall of user
ActivityFeed activityFeed = service.activities().list("me", "public").execute();
for (Activity activity : activityFeed.getItems()) {
renderResp.getWriter().println(activity.getTitle());
}
// Revoke the access token. It won't be possible to run any operations with it anymore.
// And your application will be cleared from Google applications of current user on page https://plus.google.com/apps
googleProvider.revokeToken(accessToken);
// Remove the token from the UserProfile of the current user
googleProvider.removeAccessToken(request.getRemoteUser());
Access to Provider-Specific Operations
Method
oauthProvider.getAuthorizedSocialApiObject()
is useful for obtaining access to provider-specific operations. This method usually returns objects from a 3rd party library. Those objects are always initialized with access token of the current user and can be used to retrieve data from the related social network.
Google: There are two supported types usable as arguments of this method:
com.google.api.services.plus.Plus : Google Plus API class, which can be used to call operations on Google Plus. See GoogleActivitiesPortlet and GoogleFriendsPortlet in Social Portlets example.
com.google.api.services.oauth2.Oauth2: Oauth2 class, which provides operations related to user, such as obtaining his Google user profile details or obtaining information about his access token. See GoogleUserInfoPortlet in Social Portlets example.
Twitter: There is only one supported type for Twitter: twitter4j.Twitter. An instance of this class can be used e.g. to retrieve user details, number of his tweets, number of his friends, his last tweets, etc. See TwitterPortlet in Social Portlets example.
Facebook: There is no supported type for Facebook. In Social Portlets example, we are using the 3rd party library RestFB directly to perform operations against Facebook.
Social Rest API¶
Note
The /v1/social Rest API, available as of Platform 4.3, is considered Experimental.
The API aims at giving the ability to work with Social data models via CRUD operations.
Here are some examples of what your client can feature using the API:
Create a user, change his/her password, update their profile and commit a logical deletion.
Send, cancel or accept a connection (also called relationship) request, remove a connection.
Get content of an activity stream, comment or like/unlike an activity.
Create a space, update some fields, delete the space.
Join an open space, leave the space or set a member as the space manager.
To understand how such tasks can be done by CRUD operations, the next tutorial will discuss the data models (as JSON entities). By that, you will get your hand in the abilities and the current limitation of the API.
Before starting the next tutorials, take a loot at some general characteristics. In a similar manner as Calendar Rest API, the Social API:
Uses four Http verbs: GET for Read, POST for Create, PUT for Update, and DELETE.
Uses JSON data format.
Uses the three parameters returnSize, limit and offset for paging.
When working with DateTime parameters, Calendar API uses ISO8601 format. With Social API you should always adjust the local DateTime values into UTC.
In the next tutorials, you will learn the data models, the access permission and Java/JavaScript code samples to use the API:
Social data models as JSON entities
Authorization
Java client samples
JavaScript client samples
Social data models as JSON entities¶
You might not have been well acknowledged of Social data models, but it should not block you from writing apps with proper business logics.
As it is a CRUD API, knowing that a method basically targets only one single entity, you will get into thinking how CRUD functions can be used. For example, how to “request” to connect with someone and “accept” a connection request? Here are three questions:
which data entity you work on?
which verb will be used to perform “request” and “accept”?
which fields you want to change if it is an update?
This section is a guideline for these questions. It shows you the abilities and also the (current) limitation of the API.
Note
You can go to eXo Samples Repository to view JSON samples of these tutorials.
User, username and identity Id¶
Here is a version of User as JSON entity that is returned by
GET /v1/social/users/{username}
:On Create and Update, only some of those fields are handled. So an effective version should be:
You cannot update other fields currently. However,
password
can be set in Create. It can be updated also:On Create and Update, there are some noticeable rules:
username and email must be unique.
username, email, firstName and lastName are mandatory.
Note
If you omit password field in Create method, the default password
exo
is used.You can delete a user logically. A deleted user will be no longer returned by
GET /v1/social/users
but still can be queried byGET /v1/social/users/{username}
.Username and Identity Id
From a returned User JSON, you can get username and also extract an Identity Id from the “identity” field. Both have their usage in particular.
Connection¶
A connection or a relationship between two users is established first when one of them sends a request, that actually is a connection with the status
pending
. When the receiver accepts the request, its status changes toconfirmed
.This is the full version of a Connection JSON entity:
There are two services that work on this entity. Their functionalities are basically identical.
/v1/social/relationships
/v1/social/usersRelationships
In Create and Update, you can use the exact JSON like above, or a simpler version. Pay attention to fields “sender” and “receiver” in the following examples:
Send a request. “john” is the authenticated user and is the sender.
Get “pending” requests (you need to handle the paging that is not described here).
Accept a pending request by its Id.
Cancel/Remove a connection.
Activity, like and comment¶
Here is the full version of Activity JSON returned by
GET /v1/social/activities
orGET /v1/social/activities/{activity_id}
:The
/activities
endpoint allows getting activities, reading/updating/deleting an activity. For Create, you need to use either/users/{user_id}/activities
or/spaces/{space_id}/activities
.Social allows many types of activities, but via the Rest API it makes sense to post a simple message. So an effective JSON should contain only one field “title”. See the following examples:
Create an activity in authenticated user’s activity stream (though it has username as a path param, it is not allowed to post to another user’s stream).
Create an activity in a space activity stream by the space Id.
Edit an activity by its Id:
Delete an activity by its Id:
To like or comment on an activity, use the following endpoints:
/v1/social/activities/{activity_id}/likes
/v1/social/activities/{activity_id}/comments
Like
Likes of an activity are indeed a list of users. See the JSON returned by
GET /v1/social/activities/{activity_id}/likes
:Therefore, in Create and Delete (Unlike) you do not need to send a JSON, a username is used instead. There is no Update method for a like.
To like an activity (as the authenticated user):
To unlike an activity (An administrator can delete any like. For the normal user, to delete their like, you need to set the username parameter to the authenticated user.):
Comment
The Comment(s) JSON is returned by
GET /v1/social/activities/{activity_id}/comments
orGET /v1/social/comments/{comment_id}
.In Create and Update you can use an effective version that contains only “body” field. See the following examples:
Create a comment by the activity Id:
Edit a comment by the comment Id:
Delete a comment by the comment Id:
Space and space membership¶
Here is a Space JSON returned by
GET /v1/social/spaces/{space_id}
:In Create and Update, you can use an effective version that contains several fields. See the following examples.
Create a space:
Update a space by its Id:
Delete a space by its Id:
Visibility can be
private
orhidden
. Subscription can beopen
,validation
orclose
.To get users of a space:
To get only the manager, append a query param
role=manager
:Space Membership
A membership can be a “member” or a “manager” role (A manager has both memberships).
The endpoint to work with space membership is
/v1/social/spacesMemberships
. The following examples show the abilities of this service:Get memberships of a space, using query param
space={space_display_name}
:Get memberships of a user, using query param
user={username}
:Get memberships filtered by status (all/pending/approved), using query param
status={status}
:Delete a membership by its Id. The membership Id is formed by space display name and username and role.
In Create, an effective version of the JSON entity can be used. Notice username and space display name are used.
Create membership:
The use is limited to two cases: a space manager adds a user to the space, or a user joins an open space. In the second one, the space (subscription) must be “open” and the “user” (JSON) field must be set to the authenticated user. In the both cases, the status of created membership is always “approved”.
For the same logic, it does not make sense to update a space membership though Update method is available.
Authorization¶
The API applies an access policy that is common and easy to understand, so you do not need to learn a complex ACL to handle permissions properly in your Rest client.
There is just a simple thing to learn: All authenticated users are regular users, administrators and space managers have some privileges.
Space manager
When a user creates a space, he is the space manager by default. The space manager has some privileges:
Add users to the space.
Remove users from the space.
Read all memberships of the space.
Regular users can leave spaces and can join open spaces.
You can check who is the manager of a specified space:
Platform Administrators
Members of
/platform/administrators
have some privileges:Create and (logically) delete users.
CRUD any relationship (regular users can only do with their own relationships).
Read activities of any user.
Update any space.
CRUD any space membership.
Currently there is no Rest API to directly check if the authenticated user is an administrator.
Java client samples¶
The source code of this tutorial can be found at eXo Samples Repository.
The project is a data injector that runs as a Java standalone application and does the followings:
The project consists of the following components:
A Java “main” class in which the injecting scenario is written.
A connector that basically takes care of all the work with the API. It provides basic CRUD functions and some other methods that fit the requirement in particular.
Some classes that assist the connector:
ServiceInfo
provides the URIs of the API,HttpUtils
provides Http GET/POST/PUT/DELETE methods.HttpUtils uses
HttpURLConnection
that is not described in this tutorial. Read Calendar Rest API tutorial if necessary.The data models that are POJO classes for utilizing Gson.
Login/Logout
See the login() method in the main class:
The code is explained in Authentication section.
Effective JSON
Read Calendar Rest API tutorial for utilizing Gson. However, unlike Calendar API, with current Social API you generally could not re-use a JSON object between methods. For Create/Update, you need to create “effective” JSON objects that are described in Social data models as JSON entities.
In Java, you should always create a new object, set the values for only “effective” fields before serializing and sending it. Here is an example of updating User - do not use the current user object, neither blind copy all its JSON fields:
DateTime format
When using parameters
after
andbefore
ofGET /v1/social/users/{username}/activities
, you should adjust the local DateTime to UTC and format it toyyyy-MM-dd HH:mm:ss
.Here is the code sample to request activities of the local “today”:
These query params contain white spaces, so they need to be URL-encoded:
JavaScript client samples¶
This part instructs you how to leverage these APIs to work with Social objects, including:
User
User relationship
Activity and Comment
Space and Space membership
Identity
Refer to this guide to create a gadget with the following structure:
Follow instructions there to set suitable configurations, except the file
Social-*-APIs.xml
which will be created in each part below.User¶
The source code used in this section is available here for downloading.
Searching for user
Create a file named
SocialUserAPIs.xml
under the/gadgets/SocialAPIsGadgets/
folder, then add the following script to this file:Deploy this gadget. You will see a toolbar that allows to search for user by an input string. To make it work, implement the
getUsers()
function as follows:Add the following
printUsersList()
function to print out the returned result.Deploy this gadget and create some users to test. For example, search for “john“:
Adding a new user
In addition to adding a new user via AdministrationCommunityAdd Users, you can use the Social API /rest/v1/social/users for doing this.
Add a JavaScript function with the following content:
Make a call to this function from the
getUsers()
function:Implement the
addNewUser()
function as follows:Deploy this gadget. You will see this form when clicking the Add new user button.
Call the Social API /rest/v1/social/users and pass the user information as a JSON object to add a new user. Implement the
addNewUserProcess()
as below:Deploy this gadget and test the new function.
Getting a user by remote Id
eXo Platform provides the /rest/v1/social/users/{id} API to retrieve information of a specified user. Note that the remote Id here is the user identity which in term of portal is the username.
Add a control button and a text input next to the Search for user button.
Implement the
getUserById()
function as follows:Deploy this gadget, then copy any username in the Username column of the users table (after searching for user) to test this function.
Deleting a user by remote Id
To delete a specified user, send a DELETE request to call the /rest/v1/social/users/{id} API.
Declare a global JavaScript variable named
current_user
, then add the following command to thegetUserById()
function to update this variable whenever a query on a specified user is executed:Add a control button by implementing the
addUpdateUserBtn()
function as below:Implement the
deleteUser()
function by this script:Deploy this gadget to test the new function.
Updating user information
This section shows you how to update email of a specified user by using the /rest/v1/social/users/{id} API.
Add a control button next to the Delete this user button by modifying the
addUpdateUserBtn()
function as follows:Implement the
updateUser()
function as below:Note
Only the currently logged-in user can change their email information.
Deploy this gadget, then try to change the email of the currently logged-in user.
User relationship¶
You can download all source code used in this section here.
Getting relationships of a user
To get all relationships of a specified user, use the /rest/v1/social/usersRelationships API.
Create a file named
SocialUserRelationshipAPIs.xml
under the/gadgets/SocialAPIsGadgets/
folder, then add the following script to this file:In which, the
viewRelationships()
function sends a GET request to call the /rest/v1/social/usersRelationships API that takes two input parameters includingstatus
anduser
. Thestatus
parameter can have one of 3 values: pending, confirmed and all which are corresponding to real status of a relationship.Implement the
printRelationshipsList()
to print out the returned result as follows:Deploy this gadget and test this function. You will see all relationships of the selected user are listed.
Getting a relationship by Id
Add two JavaScript functions as below:
The
selectARelationship()
function sends a GET request to the /rest/v1/social/usersRelationships/{id} API to retrieve a relationship with a specified Id.Make a call to the
selectARelationshipBtn()
function from theviewRelationships()
function.Deploy this gadget, then copy any Id in the Id column of the relationships table (after getting all relationships of the selected user) to test this function.
Updating a relationship
After selecting a specific relationship, you can change its status by using the /rest/v1/social/usersRelationships/{id} API.
Declare a global JavaScript variable named
current_relationship
to store the currently selected relationship.Add the following command to the
selectARelationship()
function to update the currently selected relationship.Add two JavaScript functions as follows:
The
updateRelationship()
function sends a PUT request to the /rest/v1/social/usersRelationships/{id} API and passes the new relationship information as a JSON object.Deploy this function and test updating any relationship status.
Deleting a relationship
To delete a relationship, send a DELETE request to the /rest/v1/social/usersRelationships/{id} API.
Add the following functions to the JavaScript part:
This will delete the selected relationship.
Make a call to the
deleteRelationshipBtn()
function by modifying theselectARelationship()
function.Deploy this gadget to test the new function.
Creating a relationship
Add the following functions to the JavaScript part of the gadget:
The
createRelationship()
function sends a POST request to the /rest/v1/social/usersRelationships API, in which the information of the new relationship is passed as a JSON object.Deploy this gadget. You will see a form like this:
This form allows to create a relationship between the currently logged-in user with another user that is not existed in the relationships list.
Activity and Comment¶
The source code used in this section is available here for downloading.
Getting activities
In this section, you are going to use two REST APIs, including:
/rest/v1/social/activities: for getting all activities of a user.
/rest/v1/social/activities/{id}: for getting a specified activity by the user Id.
Create a file named
SocialActivityAPIs.xml
under the/gadgets/SocialAPIsGadgets/
folder, then add the following script to this file:Deploy this gadget and test the Get all activities function. You will see that all activities of the currently logged-in user are listed.
Select any activity Id in the Id column of the activities table to test the Get an activity by Id function.
Getting likes and comments of an activity
Add the following functions to the JavaScript part of the gadget:
The
getNumOfLikes()
andgetNumOfComments()
functions send a GET request to call the /rest/v1/social/activities/{id}/likes and /rest/v1/social/activities/{id}/comments APIs. They return the number of likes and comments of the selected activity.Add calls to these functions in the
getActivityById()
function like this:Deploy this gadget to check these new functions.
Liking and commenting on an activity
In this section, you are going to use the following REST APIs:
/rest/v1/social/activities/{id}/likes: for liking an activity.
/rest/v1/social/activities/{id}/comments: for commenting on an activity.
Add these below functions to the JavaScript part of the gadget:
Deploy this gadget. You will see that the new functions are included.
Test these functions by:
Deleting and editing a comment
Add a control button to expand all comments of the selected activity by adding this function:
Implement the
expandComments()
andprintCommentsList()
functions as below:Deploy this gadget and test the new function by clicking the Expand all comments of this activity button. You will see that all comments are listed in a table.
Implement the
editComment()
anddeleteComment()
functions as follows:Deploy this gadget and test these functions by:
Space and Space membership¶
You can download all source code used in this section here.
Searching for space
To search for spaces that match an input string, use a GET request to call the /rest/v1/social/spaces API and pass this string to the call via a
q
parameter.Create a file named
SocialSpaceAPIs.xml
under the/gadgets/SocialAPIsGadgets/
folder, then add the following script to this file:The
searchSpace()
function returns a JSON object containing an array of spaces that match the input string. The object will then be printed out in theprintSpacesList()
function.Deploy this gadget and create some spaces to check this function. For example, search for “documentation“.
Editing a space
To edit a space, we use the /rest/v1/social/spaces/{id} API. In this section, you are going to modify the space description information.
Implement the
editSpace()
function as below:This function generates a form to enter the space description information, then packages this content in a JSON object. This object will be passed to a PUT request to call the /rest/v1/social/spaces/{id} API.
Deploy this gadget, then click the edit icon and try changing some space descriptions.
Deleting a space
To delete a space, use the /rest/v1/social/spaces/{id} API with a DELETE request.
Implement the
deleteSpace()
function as below:Deploy this gadget, then click the delete icon. The corresponding space will be deleted.
Creating a space
In this section, you are going to create a new space using the /rest/v1/social/spaces API.
Add the following functions to the JavaScript part of the gadget:
The
Add a new space
function generates a form to enter the space information. The content will then be packaged in a JSON object and passed to a POST request to call the /rest/v1/social/spaces API.Deploy this gadget and test the Add a new space function.
Getting a space by Id
eXo Platform provides the /rest/v1/social/spaces/{id} API to retrieve information of a specified space.
Add a control button and a text box next to the
Search for space
button by this script:Declare a global variable named
current_space
and implement thegetSpaceById()
function as follows:The
getUsersList()
function will be implemented in the next section.Deploy this gadget, then copy any space Id in the Id column of the spaces table (after searching for space) to test this function.
Getting users and user membership roles of a space
In this section, you are going to deal with two APIs, including:
/rest/v1/social/spaces/{id}/users: for getting all users of a space.
/rest/v1/social/spacesMemberships: for getting memberships of a user in a space.
Declare a global array variable named
roles
to store the membership roles of all users in a space.Add the following functions to the JavaScript part of the gadget:
The /rest/v1/social/spacesMemberships API requires two input parameters which are
space
- the display name of space anduser
- the username. It returns the membership roles of the user in the space.Deploy this gadget and add some users to a space to test. You will see the list of users and their memberships as below:
Editing a user membership in a space
To edit a user membership, send a PUT request to call the /rest/v1/social/spacesMemberships/{id} API.
Implement the
editMembership()
function as follows:This function requires the membership Id in format spaceName:userName:type, so you need a
getSpaceName()
function to get the space name from thecurrent_space
variable.Implement the
getSpaceName()
function as below:Deploy this gadget and test the function by clicking the edit icon, then change the current membership of any user.
Deleting a user membership in a space
To delete a user membership in a space, send a DELETE request to call the /rest/v1/social/spacesMemberships/{id} API.
Implement the
deleteMembership()
function as follows:Deploy this gadget and test the function by clicking the delete icon. The corresponding membership will be deleted.
Note
To remove users who are managers from a space, you need to delete both their member and manager memberships.
Identity and Identity relationships¶
eXo Platform uses 2 built-in types of identities, including user identities and space identities. An identity is specified by its providerId and remoteId. For user identities, the providerId is “organization“, while “space” is for space identities.
You can obviously manage the Social objects via their identities. The source code used in this section is available here for downloading.
Getting users and spaces by identity
To retrieve all users and spaces, use the /rest/v1/social/identities API with a parameter named
type
that represents the providerId of the identity.Create a file named
SocialIdentityAPIs.xml
under the/gadgets/SocialAPIsGadgets/
folder, then add the following script to this file:Deploy this gadget and test this function:
Getting a specified object by identity
To retrieve an object by its identity, use the /rest/v1/social/identities/{id} API where id is the identity of the object. Implement a
getIdentityById()
function like this:Getting relationships of an identity
You can use the /rest/v1/social/relationships API to retrieve all relationships of an identity. For example, passing a user identity and a parameter
status=pending
to the API call results in connections of this user who are in the “pending” status.Implement a function like this:
Other APIs
The remaining APIs allow to deal with Social objects (Space or User) as in the sections of User and Space APIs.
POST /v1/social/identities: creates an identity.
DELETE /v1/social/identities/{id}: deletes an identity.
PUT /v1/social/identities/{id}: updates an identity.
GET /v1/social/identities/{id}/relationships: gets the relationship between two identities.
POST /v1/social/relationships: creates a relationship between two identities.
DELETE /v1/social/relationships/{id}: deletes a relationship by Id.
PUT /v1/social/relationships/{id}: updates a relationship by Id.
GET /v1/social/relationships/{id}: gets a relationship by Id.