506 lines
18 KiB
Java
506 lines
18 KiB
Java
package org.gcube.portal.social.networking.ws.utils;
|
|
|
|
import static org.gcube.resources.discovery.icclient.ICFactory.client;
|
|
|
|
import java.io.StringReader;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
|
|
import javax.ws.rs.QueryParam;
|
|
import javax.ws.rs.core.Response;
|
|
import javax.ws.rs.core.Response.Status;
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
|
import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
|
|
import org.gcube.applicationsupportlayer.social.NotificationsManager;
|
|
import org.gcube.applicationsupportlayer.social.ex.ApplicationProfileNotFoundException;
|
|
import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
|
|
import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingUser;
|
|
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
|
|
import org.gcube.common.authorization.library.utils.Caller;
|
|
import org.gcube.common.resources.gcore.utils.XPathHelper;
|
|
import org.gcube.common.scope.api.ScopeProvider;
|
|
import org.gcube.common.scope.impl.ScopeBean;
|
|
import org.gcube.portal.databook.shared.ApplicationProfile;
|
|
import org.gcube.portal.databook.shared.Feed;
|
|
import org.gcube.portal.databook.shared.FeedType;
|
|
import org.gcube.portal.databook.shared.Post;
|
|
import org.gcube.portal.databook.shared.PrivacyLevel;
|
|
import org.gcube.portal.databook.shared.ex.FeedIDNotFoundException;
|
|
import org.gcube.portal.notifications.bean.GenericItemBean;
|
|
import org.gcube.portal.notifications.thread.MentionNotificationsThread;
|
|
import org.gcube.portal.notifications.thread.PostNotificationsThread;
|
|
import org.gcube.portal.social.networking.caches.SocialNetworkingSiteFinder;
|
|
import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
|
|
import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
|
|
import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
|
|
import org.gcube.portlets.widgets.pickitem.shared.ItemBean;
|
|
import org.gcube.resources.discovery.client.api.DiscoveryClient;
|
|
import org.gcube.resources.discovery.client.queries.api.Query;
|
|
import org.gcube.resources.discovery.client.queries.impl.QueryBox;
|
|
import org.gcube.social_networking.socialutillibrary.Utils;
|
|
import org.gcube.socialnetworking.socialtoken.SocialMessageParser;
|
|
import org.gcube.vomanagement.usermanagement.GroupManager;
|
|
import org.gcube.vomanagement.usermanagement.UserManager;
|
|
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.w3c.dom.Node;
|
|
import org.xml.sax.InputSource;
|
|
|
|
/**
|
|
* Utility class.
|
|
*/
|
|
public class SocialUtils {
|
|
|
|
// Logger
|
|
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialUtils.class);
|
|
|
|
public final static String NO_TEXT_FILE_SHARE = "_N0_73X7_SH4R3_";
|
|
public final static int CACHING_TIME_TO_EXPIRATION = 2506000;//29 days 6 minutes 40 seconds
|
|
public final static String DISABLED_USERS_NOTIFICATIONS_NAMESPACE = "dun:";
|
|
|
|
// name of the portlet for vre notification
|
|
public static final String NEWS_FEED_PORTLET_CLASSNAME = "org.gcube.portlets.user.newsfeed.server.NewsServiceImpl";
|
|
|
|
/**
|
|
*
|
|
* @param mentions the set of string containing the usernames
|
|
* @return a list of existing usernames associated with their fullnames
|
|
*/
|
|
private static ArrayList<GenericItemBean> getUsersFromUsernames(Set<String> mentions) {
|
|
if (mentions.isEmpty())
|
|
return new ArrayList<>();
|
|
ArrayList<GenericItemBean> toReturn = new ArrayList<>();
|
|
UserManager uManager = UserManagerWSBuilder.getInstance().getUserManager();
|
|
for (String username : mentions) {
|
|
try{
|
|
GCubeUser user = uManager.getUserByUsername(username);
|
|
String fullName = user.getFirstName() + " " + user.getLastName();
|
|
toReturn.add(new GenericItemBean(""+user.getUserId(), username, fullName, ""));
|
|
}catch(Exception e){
|
|
logger.error("Unable to get user informations for username=" + username);
|
|
}
|
|
}
|
|
return toReturn;
|
|
}
|
|
|
|
// utility method
|
|
private static ArrayList<ItemBean> convertToItemBean(Collection<GenericItemBean> items) {
|
|
ArrayList<ItemBean> toReturn = new ArrayList<>(items.size());
|
|
for (GenericItemBean item : items) {
|
|
toReturn.add(new ItemBean(item.getId(), item.getName(), item.getAlternativeName(), item.getThumbnailURL()));
|
|
}
|
|
return toReturn;
|
|
}
|
|
|
|
/**
|
|
* Method used when an application needs to publish something.
|
|
* @param feedText
|
|
* @param uriParams
|
|
* @param previewTitle
|
|
* @param previewDescription
|
|
* @param httpImageUrl
|
|
* @return true upon success, false on failure
|
|
*/
|
|
public static Feed shareApplicationUpdate(
|
|
String postText,
|
|
String uriParams,
|
|
String previewTitle,
|
|
String previewDescription,
|
|
String httpImageUrl,
|
|
ApplicationProfile applicationProfile,
|
|
Caller caller,
|
|
boolean notifyGroup
|
|
){
|
|
|
|
SocialMessageParser messageParser = new SocialMessageParser(postText);
|
|
String escapedPostText = messageParser.getParsedMessage();
|
|
List<String> hashtags = messageParser.getHashtags();
|
|
ArrayList<GenericItemBean> mentionedUsers = getUsersFromUsernames(Utils.getMentionedUsernames(postText));
|
|
ArrayList<ItemBean> mentionedUsersToConvertInHTML = convertToItemBean(mentionedUsers);
|
|
|
|
SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(ScopeProvider.instance.get());
|
|
|
|
escapedPostText = Utils.convertMentionUsernamesAnchorHTML(escapedPostText, mentionedUsersToConvertInHTML, site.getSiteURL(), site.getSiteLandingPagePath());
|
|
logger.info("*** Escaped post text is " + escapedPostText);
|
|
|
|
String scope = ScopeProvider.instance.get();
|
|
String appId = caller.getClient().getId();
|
|
|
|
Feed toWrite =
|
|
buildPost(
|
|
escapedPostText,
|
|
uriParams == null ? "" : uriParams,
|
|
previewTitle == null ? "" : previewTitle,
|
|
previewDescription == null ? "" : previewDescription,
|
|
httpImageUrl == null ? "" : httpImageUrl,
|
|
applicationProfile,
|
|
scope);
|
|
|
|
// try to save it
|
|
boolean res = CassandraConnection.getInstance().getDatabookStore().saveAppFeed(toWrite);
|
|
|
|
if(res){
|
|
logger.info("Feed correctly written by application " + appId);
|
|
|
|
// wait a bit before saving hashtags
|
|
if(hashtags != null && !hashtags.isEmpty())
|
|
new Thread(()->{
|
|
try {
|
|
Thread.sleep(1000);
|
|
CassandraConnection.getInstance().getDatabookStore().saveHashTags(toWrite.getKey(), scope, hashtags);
|
|
} catch (Exception e1) {
|
|
logger.error("Failed to save hashtags in Cassandra", e1);
|
|
}
|
|
}).start();
|
|
|
|
|
|
// build the notification manager
|
|
|
|
SocialNetworkingUser user = new SocialNetworkingUser(appId, "", applicationProfile.getName(), applicationProfile.getImageUrl());
|
|
NotificationsManager nm = new ApplicationNotificationsManager(
|
|
UserManagerWSBuilder.getInstance().getUserManager(),
|
|
site,
|
|
scope,
|
|
user,
|
|
NEWS_FEED_PORTLET_CLASSNAME);
|
|
|
|
if (!mentionedUsers.isEmpty())
|
|
new Thread(new MentionNotificationsThread(toWrite.getKey(), toWrite.getDescription(), nm, null, mentionedUsers)).start();
|
|
|
|
if(notifyGroup){
|
|
|
|
logger.debug("Sending notifications for " + appId + " " + scope);
|
|
|
|
try{
|
|
|
|
String name = new ScopeBean(scope).name(); // scope such as devVRE
|
|
|
|
// retrieve group information
|
|
GroupManager gManager = GroupManagerWSBuilder.getInstance().getGroupManager();
|
|
|
|
long groupId = gManager.getGroupId(name);
|
|
String groupName = gManager.getGroup(groupId).getGroupName();
|
|
|
|
logger.debug("Company id and name " + groupId + " " + groupName);
|
|
|
|
// start notification thread
|
|
new Thread(new PostNotificationsThread(
|
|
UserManagerWSBuilder.getInstance().getUserManager(),
|
|
toWrite.getKey(),
|
|
toWrite.getDescription(),
|
|
""+groupId,
|
|
nm,
|
|
new HashSet<String>(hashtags),
|
|
new HashSet<String>())
|
|
).start();
|
|
}catch (Exception e) {
|
|
logger.debug("Feed succesfully created but unable to send notifications.");
|
|
}
|
|
|
|
}
|
|
return toWrite;
|
|
}
|
|
else
|
|
return null;
|
|
|
|
}
|
|
|
|
/**
|
|
* Build an ApplicationProfile Feed.
|
|
*
|
|
* @param description add a description for the update you are sharing
|
|
* @param uriParams the additional parameters your applicationProfile needs to open the subject of this update e.g. id=12345&type=foo
|
|
* @param previewTitle the title to show in the preview
|
|
* @param previewDescription the description to show in the preview
|
|
* @param previewThumbnailUrl the image url to show in the preview
|
|
* @return a feed instance ready to be written
|
|
*/
|
|
private static Feed buildPost(
|
|
String description,
|
|
String uriParams,
|
|
String previewTitle,
|
|
String previewDescription,
|
|
String previewThumbnailUrl,
|
|
ApplicationProfile applicationProfile,
|
|
String scopeApp) {
|
|
|
|
String uri = applicationProfile.getUrl();
|
|
|
|
//add the GET params if necessary
|
|
if (uriParams != null && uriParams.compareTo("") != 0)
|
|
uri += "?"+uriParams;
|
|
|
|
Feed toReturn = new Feed(
|
|
UUID.randomUUID().toString(),
|
|
FeedType.PUBLISH,
|
|
applicationProfile.getKey(),
|
|
new Date(),
|
|
scopeApp,
|
|
uri,
|
|
previewThumbnailUrl,
|
|
description,
|
|
PrivacyLevel.SINGLE_VRE,
|
|
applicationProfile.getName(),
|
|
"no-email",
|
|
applicationProfile.getImageUrl(),
|
|
previewTitle,
|
|
previewDescription,
|
|
"",
|
|
true);
|
|
|
|
return toReturn;
|
|
}
|
|
|
|
/**
|
|
* This method looks up the applicationProfile profile among the ones available in the infrastructure
|
|
* @param idApp as identifier of your application (as reported in the ApplicationProfile)
|
|
* @param scopeApp the scope of the application
|
|
*/
|
|
public static ApplicationProfile getProfileFromInfrastrucure(String idApp, String scopeApp) {
|
|
ScopeBean scope = new ScopeBean(scopeApp);
|
|
|
|
logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + idApp + " scope: " + scope);
|
|
|
|
// set the scope of the root infrastructure
|
|
String rootInfrastructure = scopeApp.split("/")[1];
|
|
ScopeProvider.instance.set("/"+rootInfrastructure);
|
|
|
|
try {
|
|
|
|
ApplicationProfile toReturn = new ApplicationProfile();
|
|
Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " +
|
|
"where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Body/AppId/string() " +
|
|
" eq '" + idApp + "'" +
|
|
"return $profile");
|
|
|
|
|
|
DiscoveryClient<String> client = client();
|
|
List<String> appProfile = client.submit(q);
|
|
|
|
if (appProfile == null || appProfile.size() == 0)
|
|
throw new Exception("Your applicationProfile is not registered in the infrastructure");
|
|
|
|
else {
|
|
|
|
String elem = appProfile.get(0);
|
|
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
|
Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement();
|
|
XPathHelper helper = new XPathHelper(node);
|
|
|
|
List<String> currValue = null;
|
|
currValue = helper.evaluate("/Resource/Profile/Name/text()");
|
|
if (currValue != null && currValue.size() > 0) {
|
|
|
|
toReturn.setName(currValue.get(0));
|
|
|
|
}
|
|
else throw new ApplicationProfileNotFoundException("Your applicationProfile NAME was not found in the profile");
|
|
|
|
currValue = helper.evaluate("/Resource/Profile/Description/text()");
|
|
if (currValue != null && currValue.size() > 0) {
|
|
|
|
toReturn.setDescription(currValue.get(0));
|
|
|
|
}
|
|
else logger.warn("No Description exists for " + toReturn.getName());
|
|
|
|
currValue = helper.evaluate("/Resource/Profile/Body/AppId/text()");
|
|
|
|
if (currValue != null && currValue.size() > 0) {
|
|
|
|
toReturn.setKey(currValue.get(0));
|
|
|
|
}
|
|
else throw new ApplicationProfileNotFoundException("Your applicationProfile ID n was not found in the profile, consider adding <AppId> element in <Body>");
|
|
|
|
currValue = helper.evaluate("/Resource/Profile/Body/ThumbnailURL/text()");
|
|
if (currValue != null && currValue.size() > 0) {
|
|
|
|
toReturn.setImageUrl(currValue.get(0));
|
|
|
|
}
|
|
else throw new Exception("Your applicationProfile Image Url was not found in the profile, consider adding <ThumbnailURL> element in <Body>");
|
|
currValue = helper.evaluate("/Resource/Profile/Body/EndPoint/Scope/text()");
|
|
if (currValue != null && currValue.size() > 0) {
|
|
|
|
List<String> scopes = currValue;
|
|
boolean foundUrl = false;
|
|
for (int i = 0; i < scopes.size(); i++) {
|
|
if (currValue.get(i).trim().compareTo(scope.toString()) == 0) {
|
|
toReturn.setUrl(helper.evaluate("/Resource/Profile/Body/EndPoint/URL/text()").get(i));
|
|
toReturn.setScope(scope.toString());
|
|
foundUrl = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! foundUrl)
|
|
throw new ApplicationProfileNotFoundException("Your applicationProfile URL was not found in the profile for Scope: " + scope.toString());
|
|
}
|
|
|
|
else throw new ApplicationProfileNotFoundException("Your applicationProfile EndPoint was not found in the profile, consider adding <EndPoint><Scope> element in <Body>");
|
|
logger.debug("Returning " + toReturn);
|
|
return toReturn;
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e);
|
|
|
|
} finally{
|
|
|
|
// set the scope back
|
|
ScopeProvider.instance.set(scopeApp);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
/**
|
|
* Allows user to post a feed in a certain vre.
|
|
* @param userId
|
|
* @param postText
|
|
* @param vreId
|
|
* @param previewTitle
|
|
* @param previewDescription
|
|
* @param previewHost
|
|
* @param previewUrl
|
|
* @param urlThumbnail
|
|
* @param notifyGroup
|
|
* @return The written Feed
|
|
*/
|
|
public static Feed shareUserUpdate(
|
|
String userId,
|
|
String postText,
|
|
String vreId,
|
|
String previewTitle,
|
|
String previewDescription,
|
|
String previewHost,
|
|
String previewUrl,
|
|
String urlThumbnail,
|
|
boolean notifyGroup) {
|
|
|
|
SocialMessageParser messageParser = new SocialMessageParser(postText);
|
|
String escapedPostText = messageParser.getParsedMessage();
|
|
List<String> hashtags = messageParser.getHashtags();
|
|
//check if any mention exists
|
|
ArrayList<GenericItemBean> mentionedUsers = getUsersFromUsernames(Utils.getMentionedUsernames(postText));
|
|
ArrayList<ItemBean> mentionedUsersToConvertInHTML = convertToItemBean(mentionedUsers);
|
|
|
|
SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(ScopeProvider.instance.get());
|
|
|
|
escapedPostText = Utils.convertMentionUsernamesAnchorHTML(escapedPostText, mentionedUsersToConvertInHTML, site.getSiteURL(), site.getSiteLandingPagePath());
|
|
|
|
GCubeUser user;
|
|
|
|
// retrieve group information
|
|
UserManager uManager = UserManagerWSBuilder.getInstance().getUserManager();
|
|
try {
|
|
user = uManager.getUserByUsername(userId);
|
|
} catch(Exception e){
|
|
logger.error("Unable to get user informations, post write fails.", e);
|
|
return null;
|
|
}
|
|
|
|
String email = user.getEmail();
|
|
String fullName = user.getFirstName() + " " + user.getLastName();
|
|
String thumbnailURL = user.getUserAvatarURL();
|
|
String linkTitle = previewTitle == null ? "" : previewTitle;
|
|
String linkDesc = previewDescription == null ? "" : previewDescription;
|
|
String host = previewHost == null ? "" : previewHost;
|
|
String url = previewUrl == null ? "" : previewUrl;
|
|
if (urlThumbnail == null)
|
|
urlThumbnail = "null";
|
|
|
|
//this means the user has shared a file without text in it.
|
|
String textToPost = "";
|
|
if (escapedPostText.trim().compareTo(NO_TEXT_FILE_SHARE) == 0) {
|
|
textToPost = org.gcube.social_networking.socialutillibrary.Utils.convertFileNameAnchorHTML(url);
|
|
} else {
|
|
textToPost = escapedPostText;
|
|
}
|
|
|
|
Feed toShare = new Feed(UUID.randomUUID().toString(), FeedType.PUBLISH, userId, new Date(),
|
|
vreId, url, urlThumbnail, textToPost, PrivacyLevel.SINGLE_VRE, fullName, email, thumbnailURL, linkTitle, linkDesc, host);
|
|
|
|
logger.info("Attempting to save Post with text: " + textToPost + " Level = " + PrivacyLevel.SINGLE_VRE + " Timeline = " + vreId);
|
|
|
|
boolean result = CassandraConnection.getInstance().getDatabookStore().saveUserFeed(toShare);
|
|
|
|
if(vreId != null && vreId.compareTo("") != 0 && result) {
|
|
|
|
logger.trace("Attempting to write onto " + vreId);
|
|
|
|
try {
|
|
|
|
try{
|
|
logger.info("Sleeping waiting for cassandra's update");
|
|
Thread.sleep(1000);
|
|
|
|
}catch(Exception e){
|
|
|
|
logger.error(e.toString());
|
|
|
|
}
|
|
CassandraConnection.getInstance().getDatabookStore().saveFeedToVRETimeline(toShare.getKey(), vreId);
|
|
|
|
if (hashtags != null && !hashtags.isEmpty())
|
|
CassandraConnection.getInstance().getDatabookStore().saveHashTags(toShare.getKey(), vreId, hashtags);
|
|
|
|
} catch (FeedIDNotFoundException e) {
|
|
logger.error("Error writing onto VRES Time Line" + vreId);
|
|
}
|
|
logger.trace("Success writing onto " + vreId);
|
|
}
|
|
|
|
if (!result)
|
|
return null;
|
|
|
|
SocialNetworkingUser socialUser =
|
|
new SocialNetworkingUser(userId, email, fullName, thumbnailURL);
|
|
NotificationsManager nm = new ApplicationNotificationsManager(UserManagerWSBuilder.getInstance().getUserManager(), site, vreId, socialUser, NEWS_FEED_PORTLET_CLASSNAME);
|
|
if (!mentionedUsers.isEmpty())
|
|
new Thread(new MentionNotificationsThread(toShare.getKey(), toShare.getDescription(), nm, null, mentionedUsers)).start();
|
|
|
|
//send the notification about this posts to everyone in the group if notifyGroup is true
|
|
if (vreId != null && vreId.compareTo("") != 0 && notifyGroup) {
|
|
|
|
try{
|
|
// retrieve group information
|
|
GroupManager gManager = GroupManagerWSBuilder.getInstance().getGroupManager();
|
|
// handle the scope
|
|
String name = new ScopeBean(vreId).name(); // scope such as devVR
|
|
long groupId = gManager.getGroupId(name);
|
|
String groupName = gManager.getGroup(groupId).getGroupName();
|
|
|
|
logger.debug("Company id and name " + groupId + " " + groupName);
|
|
new Thread(
|
|
new PostNotificationsThread(
|
|
UserManagerWSBuilder.getInstance().getUserManager(),
|
|
toShare.getKey(),
|
|
toShare.getDescription(),
|
|
""+groupId,
|
|
nm,
|
|
new HashSet<String>(),
|
|
new HashSet<String>(hashtags))
|
|
).start();
|
|
|
|
logger.debug("Start sending notifications for post written by " + userId);
|
|
}catch(Exception e){
|
|
logger.error("Unable to notify users", e);
|
|
}
|
|
}
|
|
return toShare;
|
|
}
|
|
|
|
}
|