added linkedIn Public Profile URL import

git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/social-profile@100139 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Massimiliano Assante 2014-09-23 15:56:40 +00:00
parent 8e829f6fd7
commit 417fe2d580
9 changed files with 274 additions and 53 deletions

View File

@ -84,6 +84,11 @@
<groupId>com.googlecode.json-simple</groupId> <groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId> <artifactId>json-simple</artifactId>
</dependency> </dependency>
<dependency>
<groupId>net.eliasbalasis</groupId>
<artifactId>tibcopagebus4gwt</artifactId>
<version>1.2.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.gcube.applicationsupportlayer</groupId> <groupId>org.gcube.applicationsupportlayer</groupId>
<artifactId>aslcore</artifactId> <artifactId>aslcore</artifactId>

View File

@ -1,5 +1,10 @@
package org.gcube.portlets.user.socialprofile.client; package org.gcube.portlets.user.socialprofile.client;
import net.eliasbalasis.tibcopagebus4gwt.client.PageBusAdapter;
import net.eliasbalasis.tibcopagebus4gwt.client.PageBusAdapterException;
import net.eliasbalasis.tibcopagebus4gwt.testsubscriber.client.Person;
import net.eliasbalasis.tibcopagebus4gwt.testsubscriber.client.PersonJsonizer;
import org.gcube.portal.databook.client.GCubeSocialNetworking; import org.gcube.portal.databook.client.GCubeSocialNetworking;
import org.gcube.portal.databook.client.util.Encoder; import org.gcube.portal.databook.client.util.Encoder;
import org.gcube.portlets.user.socialprofile.client.ui.DisplayProfile; import org.gcube.portlets.user.socialprofile.client.ui.DisplayProfile;
@ -7,13 +12,13 @@ import org.gcube.portlets.user.socialprofile.client.ui.DisplaySummary;
import org.gcube.portlets.user.socialprofile.client.ui.ErrorAlert; import org.gcube.portlets.user.socialprofile.client.ui.ErrorAlert;
import org.gcube.portlets.user.socialprofile.client.ui.OkAlert; import org.gcube.portlets.user.socialprofile.client.ui.OkAlert;
import org.gcube.portlets.user.socialprofile.shared.UserContext; import org.gcube.portlets.user.socialprofile.shared.UserContext;
import org.jsonmaker.gwt.client.Jsonizer;
import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Cookies; import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.VerticalPanel;
@ -23,6 +28,7 @@ import com.google.gwt.user.client.ui.VerticalPanel;
public class SocialProfile implements EntryPoint { public class SocialProfile implements EntryPoint {
private final SocialServiceAsync socialService = GWT.create(SocialService.class); private final SocialServiceAsync socialService = GWT.create(SocialService.class);
private final PageBusAdapter pageBusAdapter = new PageBusAdapter();
private VerticalPanel mainPanel = new VerticalPanel(); private VerticalPanel mainPanel = new VerticalPanel();
private DisplayProfile dispProfile = new DisplayProfile(); private DisplayProfile dispProfile = new DisplayProfile();
@ -32,10 +38,10 @@ public class SocialProfile implements EntryPoint {
String authorizationCode = checkLinkedInAuthZ(); String authorizationCode = checkLinkedInAuthZ();
if (authorizationCode != null) { if (authorizationCode != null) {
mainPanel.add(new OkAlert("Authorization OK! Please wait while we import from LinkedIn ... ", false)); mainPanel.add(new OkAlert("Authorization OK! Please wait while we import from LinkedIn ... ", false));
socialService.fetchUserProfile(authorizationCode, DisplayProfile.getRedirectURI(), new AsyncCallback<Boolean>() { socialService.fetchUserProfile(authorizationCode, DisplayProfile.getRedirectURI(), new AsyncCallback<String>() {
@Override @Override
public void onSuccess(Boolean result) { public void onSuccess(String result) {
if (!result) { if (result == null) {
mainPanel.clear(); mainPanel.clear();
mainPanel.add(new ErrorAlert("Something went wrong while parsing your professional summary from LinkedIn, please report the issue.", true)); mainPanel.add(new ErrorAlert("Something went wrong while parsing your professional summary from LinkedIn, please report the issue.", true));
displayProfile(); displayProfile();
@ -44,6 +50,8 @@ public class SocialProfile implements EntryPoint {
mainPanel.clear(); mainPanel.clear();
mainPanel.add(new OkAlert("Your data have been imported successfully, anything you want to edit or add? Please use Edit Profile Manually.", true)); mainPanel.add(new OkAlert("Your data have been imported successfully, anything you want to edit or add? Please use Edit Profile Manually.", true));
displayProfile(); displayProfile();
//result contain the publicProfileLinkedInUrl
sendRefreshClientEvent(result);
} }
} }
@Override @Override
@ -61,6 +69,19 @@ public class SocialProfile implements EntryPoint {
RootPanel.get("SocialProfileDiv").add(mainPanel); RootPanel.get("SocialProfileDiv").add(mainPanel);
} }
protected void sendRefreshClientEvent(String inPublicProfileURL) {
//create the Contact bean data
Person person = new Person();
person.setName(inPublicProfileURL);
// publish a message with Contact bean data
try {
pageBusAdapter.PageBusPublish("net.eliasbalasis.tibcopagebus4gwt.testsubscriber.client.Person", person, (Jsonizer)GWT.create(PersonJsonizer.class));
} catch (PageBusAdapterException e) {
e.printStackTrace();
}
}
/** /**
* display the profile of the user * display the profile of the user
*/ */

View File

@ -16,5 +16,5 @@ public interface SocialService extends RemoteService {
Boolean saveIsti(String institution); Boolean saveIsti(String institution);
Boolean fetchUserProfile(String authCode, String redirectURI); String fetchUserProfile(String authCode, String redirectURI);
} }

View File

@ -12,6 +12,7 @@ public interface SocialServiceAsync {
void saveIsti(String institution, AsyncCallback<Boolean> callback); void saveIsti(String institution, AsyncCallback<Boolean> callback);
void fetchUserProfile(String authCode, String redirectURI, AsyncCallback<Boolean> callback); void fetchUserProfile(String authCode, String redirectURI,
AsyncCallback<String> callback);
} }

View File

@ -48,8 +48,12 @@ import org.slf4j.LoggerFactory;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.liferay.portal.kernel.cache.CacheRegistryUtil;
import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.model.Contact;
import com.liferay.portal.model.User; import com.liferay.portal.model.User;
import com.liferay.portal.service.ContactLocalServiceUtil;
import com.liferay.portal.service.ContactServiceUtil;
import com.liferay.portal.service.UserLocalServiceUtil; import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.theme.ThemeDisplay; import com.liferay.portal.theme.ThemeDisplay;
@ -65,7 +69,7 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
private static final String LINKEDIN_CLIEND_ID_PROPNAME = "client_id"; private static final String LINKEDIN_CLIEND_ID_PROPNAME = "client_id";
private static final String LINKEDIN_CLIEND_SECRET_PROPNAME = "client_secret"; private static final String LINKEDIN_CLIEND_SECRET_PROPNAME = "client_secret";
private static final String LINKEDIN_API_REQUEST = "https://api.linkedin.com/v1/people/~:(id,headline,summary,location:(name),industry,positions,picture-urls::(original))"; private static final String LINKEDIN_API_REQUEST = "https://api.linkedin.com/v1/people/~:(id,headline,summary,location:(name),industry,positions,picture-urls::(original),public-profile-url)";
/** /**
* the current ASLSession * the current ASLSession
@ -242,7 +246,7 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
* @return true if everything goes ok, false otherwise. * @return true if everything goes ok, false otherwise.
*/ */
@Override @Override
public Boolean fetchUserProfile(String authCode, String redirectURI) { public String fetchUserProfile(String authCode, String redirectURI) {
try { try {
HashMap<String, String> infoMap = getLinkedInUASInfo(); HashMap<String, String> infoMap = getLinkedInUASInfo();
HttpClient httpClient = HttpClients.createDefault(); HttpClient httpClient = HttpClients.createDefault();
@ -268,7 +272,7 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
String jsonText = IOUtils.toString(myInputStream, "UTF-8"); String jsonText = IOUtils.toString(myInputStream, "UTF-8");
_log.debug("LinkedIn response: " + jsonText); _log.debug("LinkedIn response: " + jsonText);
if (jsonText == null) if (jsonText == null)
return false; return null;
JSONParser parser = new JSONParser(); JSONParser parser = new JSONParser();
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -284,33 +288,33 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
Map<String, String> json = (Map<String, String>) parser.parse(jsonText, containerFactory); Map<String, String> json = (Map<String, String>) parser.parse(jsonText, containerFactory);
if (json.get("error") != null) if (json.get("error") != null)
return false; return null;
String token = json.get("access_token"); String token = json.get("access_token");
if (token == null) if (token == null)
return false; return null;
//here we got authorized by the user //here we got authorized by the user
return parseProfile(httpClient, token); return parseProfile(httpClient, token);
} }
finally { finally {
myInputStream.close(); myInputStream.close();
} }
} } else
return null;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return false; return null;
} }
return true;
} }
/** /**
* Ask the basic profile to LinkedIn API for the authenticated user, parse rge profile and write info into local DB. * Ask the basic profile to LinkedIn API for the authenticated user, parse rge profile and write info into local DB.
* @param httpClient * @param httpClient
* @param token * @param token
* @return * @return the LinkedIn public URL of the user, or null in case of errors
*/ */
private boolean parseProfile(HttpClient httpClient, String token) { private String parseProfile(HttpClient httpClient, String token) {
HttpGet request = new HttpGet(LINKEDIN_API_REQUEST); HttpGet request = new HttpGet(LINKEDIN_API_REQUEST);
// add request header as in the documentation, @see https://developer.linkedin.com/documents/authentication // add request header as in the documentation, @see https://developer.linkedin.com/documents/authentication
request.addHeader("Authorization", "Bearer " + token); request.addHeader("Authorization", "Bearer " + token);
@ -403,6 +407,12 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
pictureURL = currValue.get(0); pictureURL = currValue.get(0);
} }
String publicProfileURL = "";
currValue = helper.evaluate("/person/public-profile-url/text()");
if (currValue != null && currValue.size() > 0) {
publicProfileURL = currValue.get(0);
}
//add the positions to the summary //add the positions to the summary
summary += positions; summary += positions;
@ -422,6 +432,13 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
if (summary.compareTo("") != 0) if (summary.compareTo("") != 0)
user.setComments(escapeHtml(summary)); user.setComments(escapeHtml(summary));
//public profile URL
if (publicProfileURL.compareTo("") != 0) {
Contact contact = user.getContact();
contact.setMySpaceSn(publicProfileURL);
ContactLocalServiceUtil.updateContact(contact);
}
boolean toReturn = (UserLocalServiceUtil.updateUser(user) != null); boolean toReturn = (UserLocalServiceUtil.updateUser(user) != null);
//set the picture //set the picture
if (pictureURL.compareTo("") != 0 && pictureURL.startsWith("http")) { if (pictureURL.compareTo("") != 0 && pictureURL.startsWith("http")) {
@ -431,20 +448,25 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
UserLocalServiceUtil.updatePortrait(user.getUserId(), pictureData); UserLocalServiceUtil.updatePortrait(user.getUserId(), pictureData);
} }
} }
return toReturn; if (toReturn)
return publicProfileURL;
else return null;
} else { } else {
_log.warn("Development Mode ON, not attempting to write into DB"); _log.warn("Development Mode ON, not attempting to write into DB");
return "fakePublicURL";
} }
} }
finally { finally {
myInputStream.close(); myInputStream.close();
} }
} }
return true; else
return null;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
return false; return null;
} }
} }
@ -534,25 +556,5 @@ public class SocialServiceImpl extends RemoteServiceServlet implements SocialSer
return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;") return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
.replaceAll(">", "&gt;"); .replaceAll(">", "&gt;");
} }
//test metho
private String testParsing() {
return
"<person><id>KbR7LHuOer</id><headline>Technical Director at D4Science Hybrid Data Infrastructure</headline><summary>Senior Researcher at the Networked Multimedia Information Systems Laboratory of the &quot;Istituto di Scienza e Tecnologie della Informazione A. Faedo&quot; (ISTI) of the Italian National Research Council (CNR). I received my M.Sc. in Information Systems Technologies from the Department of Computer Science of the University of Pisa (1998), and the Ph.D. degree in Information Engineering from the Department of Information Engineering: Electronics, Information Theory, Telecommunications of the same university (2006). The aim of my research is the study and experimentation of models, methodologies and techniques for the design and development of distributed virtual research environments (VREs) which require the handling of heterogeneous computational and storage resources, provided by Grid and Cloud based e-Infrastructures, for the management of heterogenous data sources. I have a strong background on distributed architectures. I participated to the design of the most relevant distributed systems and e-Infrastructure enabling middleware developed by ISTI - CNR.I am currently the Technical Director of the Data e-Infrastructure Initiative for Fisheries Management and Conservation of Marine Living Resources (iMarine). I am also serving EUBrazilOpenBio initiative and ENVRI - Environmental Research Infrastructure - as consultant."
+"In the past, I have been involved in the Venus-C, GRDI2020, D4Science-II, D4Science, Diligent, DRIVER, DRIVER II, BELIEF, BELIEF II, Scholnet, Cyclades, and ARCA European projects.</summary>"
+"<location><name>Pisa Area, Italy</name></location><industry>Research</industry>"
+"<positions total=\"3\">"
+ "<position><id>421485155</id><title>Technical Director</title><summary>Summary D4Science.org .... the D4Science.org Agreement</summary>"
+"<start-date><year>2012</year><month>1</month></start-date><is-current>true</is-current><company><name>D4Science Hybrid Data Infrastructure</name><industry>Research</industry></company>"
+"</position>"
+ "<position><id>421485155</id><title>Senior Researcher</title><summary>Summary CNR The aim of my research is the study and experimentation of models</summary>"
+"<start-date><year>2013</year><month>4</month></start-date><is-current>true</is-current>"
+"<company><id>565371</id><name>ISTI-CNR</name><size>201-500 employees</size><type>Public Company</type><industry>Research</industry></company>"
+"</position>"
+ "<position><id>421485155</id><title>Technical Director</title><summary>Summary iMarine launches an initiative to establi</summary>"
+"<start-date><year>2014</year><month>7</month></start-date><is-current>true</is-current><company><name>D4Science Hybrid Data Infrastructure</name><industry>Research</industry></company>"
+"</position>"
+"</positions>"
+"<picture-urls total=\"1\"><picture-url key=\"original\">http://m.c.lnkd.licdn.com/mpr/mprx/0_mK4hvD9T0-r5MTo1elJa92JfJvOLMh61WlQSPuFDc3x6j3NgI8M2AQ6jLEE</picture-url></picture-urls>"
+"</person>";
}
} }

View File

@ -9,6 +9,10 @@
<inherits name='org.gcube.portlets.user.gcubewidgets.WidgetFactory' /> <inherits name='org.gcube.portlets.user.gcubewidgets.WidgetFactory' />
<inherits name='org.gcube.portlets.widgets.wsmail.WsMail_Widget' /> <inherits name='org.gcube.portlets.widgets.wsmail.WsMail_Widget' />
<inherits name='org.gcube.portal.databook.GCubeSocialNetworking' /> <inherits name='org.gcube.portal.databook.GCubeSocialNetworking' />
<inherits name="net.eliasbalasis.tibcopagebus4gwt.tibcopagebus4gwt" />
<inherits name="org.jsonmaker.gwt.Gwt_jsonmaker" />
<inherits
name="net.eliasbalasis.tibcopagebus4gwt.testsubscriber.TestSubscriber" />
<!-- Specify the app entry point class. --> <!-- Specify the app entry point class. -->
<entry-point <entry-point
class='org.gcube.portlets.user.socialprofile.client.SocialProfile' /> class='org.gcube.portlets.user.socialprofile.client.SocialProfile' />

View File

@ -2,6 +2,11 @@
<%@page pageEncoding="UTF-8"%> <%@page pageEncoding="UTF-8"%>
<script type="text/javascript">
if(window.parent.PageBus) {
window.PageBus = window.parent.PageBus;
}
</script>
<script type="text/javascript" language="javascript" src="<%=request.getContextPath()%>/socialprofile/socialprofile.nocache.js"></script> <script type="text/javascript" language="javascript" src="<%=request.getContextPath()%>/socialprofile/socialprofile.nocache.js"></script>
<div id="SocialProfileDiv"> <div id="SocialProfileDiv">
</div> </div>

View File

@ -8,6 +8,7 @@
<instanceable>false</instanceable> <instanceable>false</instanceable>
<ajaxable>false</ajaxable> <ajaxable>false</ajaxable>
<header-portlet-css>/SocialProfile.css</header-portlet-css> <header-portlet-css>/SocialProfile.css</header-portlet-css>
<header-portlet-javascript>/js/pagebus.js</header-portlet-javascript>
</portlet> </portlet>
<role-mapper> <role-mapper>
<role-name>administrator</role-name> <role-name>administrator</role-name>

View File

@ -0,0 +1,182 @@
/**
* Copyright (c) 2006-2007, TIBCO Software Inc.
* Use, modification, and distribution subject to terms of license.
*
* TIBCO(R) PageBus 1.1.0
*/
if(typeof window.PageBus == 'undefined') {
PageBus = {
version: "1.1.0",
S: {c:{},s:[]},
X: 0,
P: 0,
U: [],
H: "undefined"
};
PageBus.subscribe = function(name, scope, callback, subscriberData)
{
if(name == null)
this._badName();
if(scope == null)
scope = window;
var path = name.split(".");
var sub = { f: callback, d: subscriberData, i: this.X++, p: path, w: scope };
for(var i = 0; i < path.length; i++) {
if((path[i].indexOf("*") != -1) && (path[i] != "*") && (path[i] != "**"))
this._badName();
}
this._subscribe(this.S, path, 0, sub);
return sub;
}
PageBus.publish = function (name, message)
{
if((name == null) || (name.indexOf("*") != -1))
this._badName();
var path = name.split(".");
if(this.P > 100)
this._throw("StackOverflow");
try {
this.P++;
this._publish(this.S, path, 0, name, message);
}
catch(err) {
this.P--;
throw err;
}
try {
this.P--;
if((this.U.length > 0) && (this.P == 0)) {
for(var i = 0; i < this.U.length; i++)
this.unsubscribe(this.U[i]);
this.U = [];
}
}
catch(err) {
// All unsubscribe exceptions should already have
// been handled when unsubscribe was called in the
// publish callback. This is a repeat appearance
// of this exception. Discard it.
}
}
PageBus.unsubscribe = function(sub)
{
this._unsubscribe(this.S, sub.p, 0, sub.i);
}
/*
* @private @jsxobf-clobber
*/
PageBus._throw = function(n)
{
throw new Error("PageBus." + n);
}
/*
* @private @jsxobf-clobber
*/
PageBus._badName = function(n)
{
this._throw("BadName");
}
/*
* @private @jsxobf-clobber
*/
PageBus._subscribe = function(tree, path, index, sub)
{
var tok = path[index];
if(tok == "")
this._badName();
if(index == path.length)
tree.s.push(sub);
else {
if(typeof tree.c == this.H)
tree.c = {};
if(typeof tree.c[tok] == this.H) {
try {
tree.c[tok] = { c: {}, s: [] };
this._subscribe(tree.c[tok], path, index + 1, sub);
}
catch(err) {
delete tree.c[tok];
throw err;
}
}
else
this._subscribe( tree.c[tok], path, index + 1, sub );
}
}
/*
* @private @jsxobf-clobber
*/
PageBus._publish = function(tree, path, index, name, msg) {
if(path[index] == "")
this._badName();
if(typeof tree != this.H) {
if(index < path.length) {
this._publish(tree.c[path[index]], path, index + 1, name, msg);
this._publish(tree.c["*"], path, index + 1, name, msg);
this._call(tree.c["**"], name, msg);
}
else
this._call(tree, name, msg);
}
}
/*
* @private @jsxobf-clobber
*/
PageBus._call = function(node, name, msg) {
if(typeof node != this.H) {
var callbacks = node.s;
var max = callbacks.length;
for(var i = 0; i < max; i++)
if(callbacks[i].f != null)
callbacks[i].f.apply(callbacks[i].w, [name, msg, callbacks[i].d]);
}
}
/*
* @jsxobf-clobber
*/
PageBus._unsubscribe = function(tree, path, index, sid) {
if(typeof tree != this.H) {
if(index < path.length) {
var childNode = tree.c[path[index]];
this._unsubscribe(childNode, path, index + 1, sid);
if(childNode.s.length == 0) {
for(var x in childNode.c) // not empty. We're done.
return;
delete tree.c[path[index]]; // if we got here, c is empty
}
return;
}
else {
var callbacks = tree.s;
var max = callbacks.length;
for(var i = 0; i < max; i++) {
if(sid == callbacks[i].i) {
if(this.P > 0) {
if(callbacks[i].f == null)
this._throw("BadParameter");
callbacks[i].f = null;
this.U.push(callbacks[i]);
}
else
callbacks.splice(i, 1);
return;
}
}
// Not found. Fall through
}
}
this._throw("BadParameter");
}
}