ShowTable of Contents
Overview
IBM® Connections 4.0 has strengthened its social experience by introducing new support for OAuth 2.0 (hereafter called “OAuth2”), Activity Streams, and OpenSocial gadgets.
Combining these features together with an Embedded Experience (EE) gadget allows an application that renders a view in a context and interacts with external APIs to provide users a centralized place to go and respond to status updates from IBM Connections as well as Internet social platforms.
Facebook is the leading social network on Internet and using it together with an EE gadget can promote users' social experience with Connections Activity Streams.
This article uses Facebook as an example third-party social network platform, though the configuration and steps for other platforms are similar.
To get the most from this article, you should have a basic knowledge of OAuth2 and OpenSocial.
Before we build the EE gadget, we need to store Facebook news feeds in Connections databases and then display them on the Homepage as normal Activity Streams inside IBM Connections. When a user clicks the blank area of one Activity on the page, the EE gadget will be rendered by a gadget container with the context data from Activity Streams.
Activity Streams in IBM Connections
Let's begin with the Activity Streams format and the Connections API. Activities are serialized by use of the JavaScriptTM Object Notation (JSON) format, and the standard Activity Streams specification is defined in
activitystrea.ms. IBM Connections doesn't use all the attributes defined in that specification, and we don't cover the details here, but the main correlation attributes involving EE gadget are as shown in listing 1.
Listing 1. OpenSocial attributes in Activity Streams JSON formatting
{
…
openSocial:{
embed:{
gadget: “http://<your host>/<your context>/yourgadget.xml”,
context:{
summary:”summary example”,
userId: “<user-uuid>”,
activityId:””
...
}
}
}
}
In listing 1, all the “context” object content can be passed through the OpenSocial container to your EE gadget. The schema inside “context” has no limitation, and you can add any key-value pairs to it.
Users can retrieve and post their Activity Streams data, using the Activity Streams API:
<connections-context>/opensocial/rest/activitystreams/@me/@all/
To successfully post Activity Streams entries from Facebook and retrieve them later, you must register a third-party application ID for Facebook. Refer the steps in the product documentation topic, “
Registering third-party applications,” for more details.
If you register with the appId, “facebook”, then when posting data you should set generator[“id”] to “facebook” in the Activity Streams entry and use the API, /@me/@all/facebook, to retrieve them. Meanwhile the Hompage Activity Streams also reflects the application you added, as shown in figure 1.
Figure 1. Activity Streams filtered by “facebook”
The most popular social platform has adopted OAuth2 as its security protection for open APIs. Before getting into building the EE gadget, you should become familiar with the OAuth2 protocol, to help you achieve the correct configuration and avoid confusion while troubleshooting.
OAuth2 flows
Here we give a brief introduction to the authentication flows that Connections OpenSocial gadget implements (see figure 2). Three-legged OAuth involves the social network server, the application server, and the user which, in this article, are defined as follows:
- Social network server: Facebook API plays the role of service provider.
- Application server: Connections OpenSocial container
- User: User's browser
Note that, in Step 3 in the figure, a predefined URL of app server must be sent with the login request so that Facebook knows where to redirect.
Figure 2. Authentication flows outline
After the Connections server obtains the access_token, Connections News saves the access_token in the database for persistence. Users won't need repeat the OAuth flows every time the Facebook API is requested, until the access_token expires.
Building an OpenSocial gadget for Facebook
Let's now build our gadget.
Initialization and configuration
As illustrated in the above OAuth flows section, the Service Provider, the Consumer client, and the bindings relationship between them should be registered first. In this case we set providerName to “facebook”, clientName to “facebook_client1”, and serviceName to “facebook_service”.
- Create an app on the Facebook developers website and record the App ID\Secret (see figure 3).
- Make sure to set the App Domains to your Connections domain, and under the “Select how your app integrates with Facebook” section, select “Website with Facebook Login”.
- In the Site URL field, enter your Connections domain plus port. Facebook will only redirect URLs to this domain.
Figure 3. Facebook App creation
4. Start the wsadmin client to execute the registration commands. Refer to the topic, “
Starting the wsadmin client,” for details.
5. Register the service provider and consumer as follows:
a) Access the News Admin script, wsadmin>execfile(“newsAdmin.py”), and register the Service provider-associated parameters:
providerName clientAuth authHeader urlParam authUrl tokenUrl
b) Register the app with the ID\Secret from Step 1 as client. Here are the associated parameters:
clientName providerName ctype grantType clientID clientSecret redirectUri
c) Set the clientID\clientSecret with the App ID\Secret from Step 1:
wsadmin>NewsOAuth2ConsumerService.registerClient("facebook_client1","facebook","confidential","code","<your Facebook App ID>","<your Facebook App secret>","https://{opensocial}/gadgets/oauth2callback")
6. Add the gadget to the Connections News application:
wsadmin>
NewsWidgetCatalogService.addWidget(title="Facebook EE gadget", text="Facebook EE For Activity Streams.", url="
http://test.host.com/gadget/FacebookEE.xml",categoryName= [WidgetCategories.NONE], isGadget=TRUE,appContexts= [WidgetContexts.EMBEDXP], policyFlags= [GadgetPolicyFlags.TRUSTED], prereqs= [])
The gadget content .xml URL is needed here for configuration, so you now should decide where to place the gadget content xml file. You can use a Web server other than that hosts Connections; for example:
We will introduce how to write this .xml in the following section.
7. Record the gadget ID returned by this command. Considering security, production environments may disallow the gadget to access any internal or external Web sites except the Facebook API service. To make this restriction, add one more key-value parameter “proxyPolicy=custom” to the command, referring to “
Configuring per-host proxy access rules for OpenSocial gadgets”.
You can also refer to the topic, “
Administering gadgets for IBM Connections and widgets for Home page,” to add/edit the custom gadget; however, you'll need to check the .html source to find out the gadget ID.
8. Finally, bind the Facebook EE gadget:
associated parameters: widgetId serviceName clientName
allowModuleOverridewsadmin>NewsOAuth2ConsumerService.bindGadget("<gadget ID from previous step>", "facebook_service", "facebook_client1", "false")“serviceName”
set here will be referred to in the gadget content xml for declaring access to specific service. “clientName” should be set to the same value as in Step 5.c.
We have completed all the necessary OAuth and OpenSocial gadget-related configuration steps.
NOTE: One potential issue you may encounter is that the Connections server can't get a response from Facebook API. If you understand WebSphere Application Server's (WAS) security mechanism, you can easily determine the cause: A third-part API is usually transported through an SSL-based HTTP channel, so WAS needs to import the SSL certificate from the Facebook API server.
Develop Facebook EE gadget
For its self-owned applications, a Connections 4.0 EE gadget displays more details, including comments, files preview, and recent updates. Users can reply to an entry, for instance, and share a file without jumping to each application's pages. Here we introduce how to retrieve and comment on a post for Facebook.
Listing 2 shows the necessary features required by the EE gadget and declares the OAuth2 service, where:
- Service name is “facebook_service”, which was defined above.
- Scope is used to restrict the permission level of APIs protected under OAuth. The values for scope differ significantly among the different social network providers. For Facebook, “read_stream” and “ publish_stream” are required to retrieve and post data.
Listing 2. OAuth2 declaration
<ModulePrefs title="Facebook EE Gadget"
description="Facebook Embedded Experience Gadget">
<Require feature="opensocial-0.9"/>
<Require feature="osapi" />
<Require feature="embedded-experiences" />
<Require feature="oauthpopup" />
<OAuth2>
<Service name="facebook_service" scope="read_stream,publish_stream">
</Service>
</OAuth2>
</ModulePrefs>
Now let's focus on the context, first mentioned above in the Section 2. Listing 3 shows how the initData function is used to load the context from a gadget container.
Listing 3. EE context loading
var entryContext;
function loadContextData(context){
entryContext = context;
}
function loadFacebookData(){...}
function onLoad(context){
loadContextData(context);
loadFacebookData();
}
function initData() {
opensocial.data.getDataContext()
.registerListener('org.opensocial.ee.context', function(key){
onLoad(opensocial.data.getDataContext().getDataSet(key));
});
}
gadgets.util.registerOnLoadHandler(initData);
The purpose of loading the context here is to get the object id of the Facebook entry posted to Connections Activity Streams. You can retrieve details of that entry by using the Facebook Graph API,
https://graph.facebook.com/<object-id>. To post a comment to one entry, use the
https://graph.facebook.com/<entry-id>/comments API.
In Listing 4, the loadFacebookData function is invoked immediately after the EE context is loaded. The gadget calls “gadgets.io.makeRequest” and delegates the Facebook API request to the gadget container backend. Before the gadget sends this request, the OAuth flows would have been followed, if no corresponding authorization access_token exists.
Listing 4. Get user's authorization and send API request
function showResults(result) {
var entry = result.data;
document.getElementById('main').innerHTML =
"<div>" + entry.from.name +" " + entry.from. category + " shared a link <br/>"
+ '<a href="' + entry.link +'" >'+entry.name+'</a><br />'
+ '<img width=""src="'+ entry.picture+'"><br />'
+ '<span>' + entry.description+ '</span>'
+ '</div><div style="border-top: 1px solid #AAA;margin-top:10px">'
+ 'Add a Comment:<br/><textarea rows=5 cols=25 id=commentText></textarea><br/><button id=postComment >Post</button></div>';
document.getElementById('postComment').onclick = postComment;
toggleDivShow('main', false);
}
function passOAuth(response){
var onOpen = function() {
toggleDivShow('topermit',false);
};
var onClose = function() {
loadFacebookData();
};
var popup = new gadgets.oauth.Popup(response.oauthApprovalUrl, null, onOpen, onClose);
document.getElementById('needOauthLink').onclick = popup.createOpenerOnClick();
document.getElementById('withpermit').onclick = popup.createApprovedOnClick();
toggleDivShow('needOauth', false)
}
function loadFacebookData(){
var params = {};
var url = "https://graph.facebook.com/" + entryContext["facebook-id"];
params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "facebook_service";
params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.JSON;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH2;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.GET;
params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = "0";
gadgets.io.makeRequest(url, function (response) {
if (response.oauthApprovalUrl) {
passOAuth(response);
} else if (response.data || response.text) {
showResults(response);
} else {
console.log('OAuth error: '+response.oauthError + ': ' + response.oauthErrorText);
}
}, params);
}
Facebook and IBM Connections are now ready for processing the flows. We should first handle the browser-side operations.
In the “passOAuth” function, “gadgets.oauth.Popup” is responsible for popping up the Facebook window and calling back functions of the EE gadget. Figure 4 shows the link we added to indicate OAuth is required. Clicking the link opens the Facebook Auth window. The browser will go back to the Connections Homepage, if the user approve this access by clicking the Go to App button.
Figure 4. Facebook OAuth window
The showResults function renders the UI with data returned from the Facebook API. Figure 5 shows a sample UI example.
Figure 5. Sample EE UI
In Listing 5 the postComment function making the comment back to Facebook entry is attached to the Post button. Here we need a POST request and the obvious differences from above are the parameters settings. For details of these parameters, refer to the
OpenSocial specification.
Listing 5. Post comment to Facebook entry
function postComment(){
var params = {};
var postData = {"message" : document.getElementById("commentText").value};
postData = gadgets.io.encodeValues(postData);
var url = "https://graph.facebook.com/" + entryContext["facebook-id"] + "/comments";
params[gadgets.io.RequestParameters.POST_DATA]= postData;
params[gadgets.io.RequestParameters.OAUTH_SERVICE_NAME] = "facebook_service";
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.OAUTH2;
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
gadgets.io.makeRequest(url, function (response) {
if (response.oauthApprovalUrl) {
passOAuth(response);
} else if (response.id) {
alert("Comment Successfully!");
} else {
console.log('OAuth error: '+response.oauthError + ': ' + response.oauthErrorText);
}
}, params);
}
Conclusion
The Embedded Experience gadget in Connections 4.0 Activity Streams greatly enriches the Connections user experience, providing them with the full freedom to customize and facilitate integration with any other applications safely. What we've demonstrated here with Facebook is also applicable to other third-part platforms.
Tell us what you think
Please visit this link to take a one-question survey about this article:
Resources
About the authors
Tao Shang is a Software Engineer working on the Connections Development team based at IBM's China Development Laboratory (CDL). He participated in implementing Events Gadgets and Homepage Activity Streams for IBM Connections 4.0 and has more than four years of project experience in JAVA EE. You can reach him at
shtaosh@cn.ibm.com.
Rui Qi Shi is a Software Engineer working on the Connections Development team based at IBM's CDL. He helped develop the Homepage Activity Streams for Connections 4.0 and has rich experience in Web and Java EE development. You can reach him at
shiruiqi@cn.ibm.com.