news-feed/src/main/java/org/gcube/portlets/user/newsfeed/server/NewsServiceImpl.java

625 lines
21 KiB
Java

package org.gcube.portlets.user.newsfeed.server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import org.gcube.application.framework.core.session.ASLSession;
import org.gcube.application.framework.core.session.SessionManager;
import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
import org.gcube.applicationsupportlayer.social.NotificationsManager;
import org.gcube.common.core.utils.logging.GCUBEClientLog;
import org.gcube.portal.custom.communitymanager.OrganizationsUtil;
import org.gcube.portal.custom.scopemanager.scopehelper.ScopeHelper;
import org.gcube.portal.databook.server.DBCassandraAstyanaxImpl;
import org.gcube.portal.databook.server.DatabookStore;
import org.gcube.portal.databook.shared.Comment;
import org.gcube.portal.databook.shared.Feed;
import org.gcube.portal.databook.shared.Like;
import org.gcube.portal.databook.shared.UserInfo;
import org.gcube.portal.databook.shared.ex.ColumnNameNotFoundException;
import org.gcube.portal.databook.shared.ex.FeedIDNotFoundException;
import org.gcube.portal.databook.shared.ex.FeedTypeNotFoundException;
import org.gcube.portal.databook.shared.ex.PrivacyLevelTypeNotFoundException;
import org.gcube.portlets.user.newsfeed.client.NewsService;
import org.gcube.portlets.user.newsfeed.shared.EnhancedFeed;
import org.gcube.vomanagement.usermanagement.GroupManager;
import org.gcube.vomanagement.usermanagement.impl.liferay.LiferayGroupManager;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.model.Organization;
import com.liferay.portal.model.Role;
import com.liferay.portal.model.User;
import com.liferay.portal.model.UserModel;
import com.liferay.portal.service.OrganizationLocalServiceUtil;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.theme.ThemeDisplay;
/**
* The server side implementation of the RPC service.
*/
@SuppressWarnings("serial")
public class NewsServiceImpl extends RemoteServiceServlet implements NewsService {
private static GCUBEClientLog _log = new GCUBEClientLog(NewsServiceImpl.class);
/**
*
*/
private static final String ADMIN_ROLE = "Administrator";
/**
*
*/
private static final String SESSION_ADMIN_ATTR = "SESSION_ADMIN_ATTR";
private DatabookStore store;
private boolean withinPortal = false;
private final static int MAX_FEEDS_NO = 20;
//this is the cache for the user portraitsId that change among portals, the cache avoids to continuosly ask the LR DB the portraitID;
private HashMap<String, Long> portraitIdCache;
public void init() {
store = new DBCassandraAstyanaxImpl();
portraitIdCache = new HashMap<String, Long>();
}
public void destroy() {
store.closeConnection();
}
/**
* the current ASLSession
* @return the session
*/
private ASLSession getASLSession() {
String sessionID = this.getThreadLocalRequest().getSession().getId();
String user = (String) this.getThreadLocalRequest().getSession().getAttribute(ScopeHelper.USERNAME_ATTRIBUTE);
if (user == null) {
_log.warn("USER IS NULL setting testing user and Running OUTSIDE PORTAL");
//user = "test.user";
user = "massimiliano.assante";
}
else {
withinPortal = true;
}
return SessionManager.getInstance().getASLSession(sessionID, user);
}
@Override
public ArrayList<EnhancedFeed> getAllUpdateUserFeeds() {
String userName = getASLSession().getUsername();
ArrayList<Feed> toMerge = new ArrayList<Feed>();
HashMap<String, Feed> feedsMap = new HashMap<String, Feed>();
try {
if (!withinPortal) {
return getEclipseResult(userName, false);
}
else {
_log.info("****** retrieving feeds for user: " + userName);
User currUser = OrganizationsUtil.validateUser(userName);
//VRE Feeds
for (Organization org : currUser.getOrganizations()) {
GroupManager gm = new LiferayGroupManager();
if (gm.isVRE(org.getOrganizationId()+"")) {
String vreid = gm.getScope(""+org.getOrganizationId()); //get the scope
_log.trace("Reading feeds for VRE: " + vreid);
ArrayList<Feed> OrganizationFeeds = (ArrayList<Feed>) store.getRecentFeedsByVRE(vreid, 10);
for (Feed feed : OrganizationFeeds) {
feedsMap.put(feed.getKey(), feed);
}
}
}
//User Own Feeds
ArrayList<Feed> userFeeds = (ArrayList<Feed>) store.getRecentFeedsByUser(userName, 10);
for (Feed feed : userFeeds) {
feedsMap.put(feed.getKey(), feed);
}
//UserFriends Feeds
ArrayList<String> userFriendsIds = (ArrayList<String>)store.getFriends(userName);
for (String userid : userFriendsIds) {
for (Feed feed : store.getRecentFeedsByUser(userid, 10)) {
feedsMap.put(feed.getKey(), feed);
}
}
//Portal Feeds
ArrayList<Feed> portalFeeds = (ArrayList<Feed>) store.getAllPortalPrivacyLevelFeeds();
for (Feed feed : portalFeeds) {
feedsMap.put(feed.getKey(), feed);
}
for (String key: feedsMap.keySet()) {
toMerge.add(feedsMap.get(key));
}
}
ArrayList<Feed> toReturn = new ArrayList<Feed>();
//return only <MAX_FEEDS_NO> feeds
if (toMerge.size() > MAX_FEEDS_NO)
for (int i = 0; i < MAX_FEEDS_NO; i++)
toReturn.add(toMerge.get(i));
else {
return enhanceFeeds(toMerge);
}
return enhanceFeeds(toReturn);
} catch (PrivacyLevelTypeNotFoundException e) {
_log.error("Privacy Level not Found " + e.getMessage());
e.printStackTrace();
} catch (FeedTypeNotFoundException e) {
_log.error("Feed Type not Found " + e.getMessage());
e.printStackTrace();
} catch (ColumnNameNotFoundException e) {
_log.error("Column name not Found " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* return only the user connection feeds
*/
@Override
public ArrayList<EnhancedFeed> getOnlyConnectionsUserFeeds() {
ArrayList<Feed> toMerge = new ArrayList<Feed>();
HashMap<String, Feed> feedsMap = new HashMap<String, Feed>();
String userName = getASLSession().getUsername();
try {
if (! withinPortal) {
return getEclipseResult(userName, true);
}
else {
//UserFriends Feeds
ArrayList<String> userFriendsIds = (ArrayList<String>)store.getFriends(userName);
for (String userid : userFriendsIds) {
for (Feed feed : store.getRecentFeedsByUser(userid, 10)) {
feedsMap.put(feed.getKey(), feed);
}
}
for (String key: feedsMap.keySet()) {
toMerge.add(feedsMap.get(key));
}
Collections.sort(toMerge, Collections.reverseOrder());
ArrayList<Feed> toReturn = new ArrayList<Feed>();
//return only <MAX_FEEDS_NO> feeds
if (toMerge.size() > MAX_FEEDS_NO)
for (int i = 0; i < MAX_FEEDS_NO; i++)
toReturn.add(toMerge.get(i));
else
return enhanceFeeds(toMerge);;
}
} catch (PrivacyLevelTypeNotFoundException e) {
_log.error("Privacy Level not Found " + e.getMessage());
e.printStackTrace();
} catch (FeedTypeNotFoundException e) {
_log.error("Feed Type not Found " + e.getMessage());
e.printStackTrace();
} catch (ColumnNameNotFoundException e) {
_log.error("Column name not Found " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* just for testing purposes
*
* @param userName
* @return
* @throws PrivacyLevelTypeNotFoundException
* @throws FeedTypeNotFoundException
* @throws ColumnNameNotFoundException
* @throws FeedIDNotFoundException
*/
private ArrayList<EnhancedFeed> getEclipseResult(String userName, boolean onlyConnections) throws PrivacyLevelTypeNotFoundException, FeedTypeNotFoundException, ColumnNameNotFoundException, FeedIDNotFoundException {
ArrayList<Feed> toMerge = new ArrayList<Feed>();
HashMap<String, Feed> feedsMap = new HashMap<String, Feed>();
ArrayList<Feed> OrganizationFeeds = (ArrayList<Feed>) store.getRecentFeedsByVRE("/gcube/devsec/devVRE", 10);
for (Feed feed : OrganizationFeeds) {
feedsMap.put(feed.getKey(), feed);
}
if (! onlyConnections) {
//User Own Feeds
ArrayList<Feed> userFeeds = (ArrayList<Feed>) store.getRecentFeedsByUser(userName, 10);
for (Feed feed : userFeeds)
feedsMap.put(feed.getKey(), feed);
// //Portal Feeds
ArrayList<Feed> portalFeeds = (ArrayList<Feed>) store.getAllPortalPrivacyLevelFeeds();
for (Feed feed : portalFeeds)
feedsMap.put(feed.getKey(), feed);
}
//UserFriends Feeds
ArrayList<String> userFriendsIds = (ArrayList<String>)store.getFriends(userName);
for (String userid : userFriendsIds) {
for (Feed feed : store.getRecentFeedsByUser(userid, 10)) {
feedsMap.put(feed.getKey(), feed);
}
}
for (String key: feedsMap.keySet()) {
toMerge.add(feedsMap.get(key));
}
for (Feed feed : toMerge) {
feed.setThumbnailURL("http://127.0.0.1:8888/images/Avatar_default.png");
}
return enhanceFeeds(toMerge);
}
@Override
public ArrayList<EnhancedFeed> getOnlyMyUserFeeds() {
String userName = getASLSession().getUsername();
_log.trace("getOnly UserFeeds for " + userName);
ArrayList<Feed> userFeeds = null;
try {
userFeeds = (ArrayList<Feed>) store.getRecentFeedsByUser(userName, 15);
} catch (Exception e) {
e.printStackTrace();
}
return enhanceFeeds(userFeeds);
}
@Override
public ArrayList<EnhancedFeed> getOnlyLikedFeeds() {
String userName = getASLSession().getUsername();
_log.trace("getLiked Feeds for " + userName);
ArrayList<Feed> userFeeds = null;
try {
userFeeds = (ArrayList<Feed>) store.getAllLikedFeedsByUser(userName, 25);
} catch (Exception e) {
e.printStackTrace();
}
return enhanceFeeds(userFeeds);
}
@Override
public boolean like(String feedid, String feedText, String feedOwnerId) {
boolean likeCommitResult = false;
UserInfo user = getUserInfo();
Like toLike = new Like(UUID.randomUUID().toString(), user.getUsername(),
new Date(), feedid, user.getFullName(), user.getAvatarId());
try {
likeCommitResult = store.like(toLike);
} catch (FeedIDNotFoundException e) {
_log.error("Feed not Found for this like " + e.getMessage());
e.printStackTrace();
return false;
}
//if the like was correctly delivered notify users involved
if (likeCommitResult) {
//if the user who liked this post is not the user who posted it notify the poster user (Feed owner)
if (! user.getUsername().equals(feedOwnerId)) {
NotificationsManager nm = new ApplicationNotificationsManager(getASLSession());
boolean nResult = nm.notifyLikedFeed(feedOwnerId, feedid, escapeHtml(feedText));
_log.trace("Like Notification added? " + nResult);
}
}
return likeCommitResult;
}
/**
* @param feedid the id of the commented feed
* @param commentText the comment text
* @param feedOwnerId the username of the user who created the post that was commented
*/
@Override
public Comment comment(String feedid, String commentText, String feedOwnerId) {
boolean commentCommitResult = false;
_log.trace("Trying to add this comment " + commentText);
UserInfo user = getUserInfo();
Comment comment = new Comment(UUID.randomUUID().toString(), user.getUsername(),
new Date(), feedid, escapeHtml(commentText), user.getFullName(), user.getAvatarId());
try {
if (store.addComment(comment))
commentCommitResult = true;
} catch (FeedIDNotFoundException e) {
_log.error("Feed not Found for this comment " + e.getMessage());
e.printStackTrace();
return null;
}
//if the comment was correctly delivered notify users involved
if (commentCommitResult) {
//if the user who commented this post is not the user who posted it notify the poster user (Feed owner)
NotificationsManager nm = new ApplicationNotificationsManager(getASLSession());
if (! user.getUsername().equals(feedOwnerId)) {
boolean result = nm.notifyOwnCommentReply(feedOwnerId, feedid, escapeHtml(commentText));
_log.trace("Comment Notification to post owner added? " + result);
}
//if there are other users who liked this post they get notified too, asynchronously with this thread
Thread thread = new Thread(new NotificationsThread(commentText, nm, getAllLikesByFeed(feedid)));
thread.start();
}
return comment;
}
private String replaceAmpersand(String toReplace) {
String toReturn = toReplace.replaceAll("&amp;", "&");
return toReturn;
}
/**
* this method sorts the Feeds in Chronological Reversed order and adds additional user informations
* @param toEnhance
* @return
*/
private ArrayList<EnhancedFeed> enhanceFeeds(ArrayList<Feed> toEnhance) {
ArrayList<EnhancedFeed> toReturn = new ArrayList<EnhancedFeed>();
String username = getASLSession().getUsername();
//sort the Feeds
Collections.sort(toEnhance, Collections.reverseOrder());
ArrayList<String> likedFeeds = (ArrayList<String>) store.getAllLikedFeedIdsByUser(getASLSession().getUsername());
// System.out.println("Liked Feed for " + username);
// for (String liked : likedFeeds) {
// System.out.println(liked);
// }
for (Feed feed : toEnhance) {
feed.setDescription(replaceAmpersand(feed.getDescription()));
if (! feed.isApplicationFeed())
feed.setThumbnailURL(getUserImagePortraitUrlLocal(feed.getEntityId()));
//if likedFeeds contains this feed key it means the user already Liked it
boolean liked = likedFeeds.contains(feed.getKey());
int commentsNo = 0;
try {
commentsNo = Integer.parseInt(feed.getCommentsNo());
}
catch (NumberFormatException e) {
commentsNo = 0;
_log.error("NumberFormatException while reading comments number " + e.getMessage());
}
if (commentsNo == 0) {
EnhancedFeed toAdd = null;
//create the enhanced feed
if (feed.isApplicationFeed()) {
toAdd = new EnhancedFeed(feed, liked, checkisAdminUser());
} else
toAdd = new EnhancedFeed(feed, liked, isUsers(feed, username));
toReturn.add(toAdd);
} else {
ArrayList<Comment> comments = getAllCommentsByFeed(feed.getKey());
//sort in chronological order
Collections.sort(comments);
//TODO: retrieve only the first 2 more recents efficiently from the store
//only the first two first 2 more recent returned to the client initially
int currCommentsNumber = comments.size();
//if comments are less than 2 they are the more recent
if (currCommentsNumber < 2) {
EnhancedFeed toAdd = new EnhancedFeed(feed, liked, isUsers(feed, username), comments);
toReturn.add(toAdd);
} else {
//need to get the last two
ArrayList<Comment> comments2Attach = new ArrayList<Comment>();
for (int i = currCommentsNumber -2; i < currCommentsNumber; i++) {
comments2Attach.add(comments.get(i));
}
EnhancedFeed toAdd = new EnhancedFeed(feed, liked, isUsers(feed, username), comments2Attach);
toReturn.add(toAdd);
}
}
}
_log.trace("****** ENHANCED FEEDS TOTAL= " + toReturn.size());
return toReturn;
}
/**
* this method is needed because user images portrait change id depending on the portal instance
* e.g. a post made from iMarine portal would not show the avatarIMage in D4Science.org
* @param screenname
* @return the url of the image portrait for this portal instance
*/
private String getUserImagePortraitUrlLocal(String screenName) {
if (! withinPortal) {
return "";
}
StringBuilder thumbnailURL = new StringBuilder("/image/user_male_portrait?img_id=");
// if (portraitIdCache.containsKey(screenName))
// return thumbnailURL.append(portraitIdCache.get(screenName)).toString();
// else {
User user = null;
try {
user = UserLocalServiceUtil.getUserByScreenName(OrganizationsUtil.getCompany().getCompanyId(), screenName);
// portraitIdCache.put(screenName, user.getPortraitId());
} catch (PortalException e) {
e.printStackTrace();
} catch (SystemException e) {
e.printStackTrace();
}
return thumbnailURL.append(user.getPortraitId()).toString();
// }
}
@Override
public UserInfo getUserInfo() {
if (getUserFromSession() != null)
return getUserFromSession();
try {
String username = getASLSession().getUsername();
String email = username+"@isti.cnr.it";
String fullName = username+" FULL";
String thumbnailURL = "images/Avatar_default.png";
if (withinPortal) {
UserModel user = UserLocalServiceUtil.getUserByScreenName(OrganizationsUtil.getCompany().getCompanyId(), username);
thumbnailURL = "/image/user_male_portrait?img_id="+user.getPortraitId();
fullName = user.getFirstName() + " " + user.getLastName();
email = user.getEmailAddress();
ThemeDisplay themeDisplay = (ThemeDisplay) this.getThreadLocalRequest().getSession().getAttribute(WebKeys.THEME_DISPLAY);
String accountURL = themeDisplay.getURLMyAccount().toString();
UserInfo toReturn = new UserInfo(username, fullName, thumbnailURL, user.getEmailAddress(), accountURL, true, false, null);
setUserInSession(toReturn);
return toReturn;
}
else {
_log.info("Returning test USER");
return new UserInfo(getASLSession().getUsername(), fullName, thumbnailURL, email, "fakeAccountUrl", true, false, null);
}
} catch (Exception e) {
e.printStackTrace();
}
return new UserInfo();
}
@Override
public ArrayList<Like> getAllLikesByFeed(String feedid) {
ArrayList<Like> toReturn = (ArrayList<Like>) store.getAllLikesByFeed(feedid);
_log.trace("Asking likes for " + feedid);
for (Like like : toReturn) {
like.setThumbnailURL(getUserImagePortraitUrlLocal(like.getUserid()));
}
return toReturn;
}
@Override
public ArrayList<Comment> getAllCommentsByFeed(String feedid) {
_log.trace("Asking comments for " + feedid);
ArrayList<Comment> toReturn = (ArrayList<Comment>) store.getAllCommentByFeed(feedid);
for (Comment comment : toReturn) {
comment.setThumbnailURL(getUserImagePortraitUrlLocal(comment.getUserid()));
}
Collections.sort(toReturn);
return toReturn;
}
@Override
public boolean deleteComment(String commentid, String feedid) {
_log.trace("Attempting to delete comment " + commentid);
try {
return store.deleteComment(commentid, feedid);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public boolean deleteFeed(String feedid) {
_log.trace("Attempting to delete feed " + feedid);
try {
return store.deleteFeed(feedid);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private UserInfo getUserFromSession() {
return (UserInfo) getASLSession().getAttribute(UserInfo.USER_INFO_ATTR);
}
private void setUserInSession(UserInfo user) {
getASLSession().setAttribute(UserInfo.USER_INFO_ATTR, user);
}
/**
* tell if a feed is belonging to the current user or not
* @param tocheck
* @param username
* @return true if this feed is of the current user
*/
private boolean isUsers(Feed tocheck, String username) {
return (tocheck.getEntityId().equals(username));
}
/**
* Escape an html string. Escaping data received from the client helps to
* prevent cross-site script vulnerabilities.
*
* @param html the html string to escape
* @return the escaped string
*/
private String escapeHtml(String html) {
if (html == null) {
return null;
}
return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}
@Override
public Comment editComment(Comment toEdit) {
UserInfo user = getUserInfo();
Comment edited = new Comment(toEdit.getKey(), toEdit.getUserid(),
new Date(), toEdit.getFeedid(), escapeHtml(toEdit.getText()), user.getFullName(), user.getAvatarId());
try {
store.editComment(edited);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return edited;
}
/**
*
* @return true if the user is a portal administrator or not
*/
private boolean checkisAdminUser() {
if (getASLSession().getAttribute(SESSION_ADMIN_ATTR) == null) {
boolean isAdmin = false;
try {
isAdmin = isAdmin();
} catch (Exception e) {
e.printStackTrace();
}
getASLSession().setAttribute(SESSION_ADMIN_ATTR, isAdmin);
return isAdmin;
}
return (Boolean) getASLSession().getAttribute(SESSION_ADMIN_ATTR);
}
/**
* tell if the user is a portal administrator or not
* @param username
* @return true if is admin
* @throws SystemException
* @throws PortalException
*/
private boolean isAdmin() throws PortalException, SystemException {
User currUser = OrganizationsUtil.validateUser(getASLSession().getUsername());
List<Organization> organizations = OrganizationLocalServiceUtil.getOrganizations(0, OrganizationLocalServiceUtil.getOrganizationsCount());
Organization rootOrganization = null;
for (Organization organization : organizations) {
if (organization.getName().equals(OrganizationsUtil.getRootOrganizationName() ) ) {
rootOrganization = organization;
break;
}
}
try {
_log.trace("root: " + rootOrganization.getName() );
return (hasRole(ADMIN_ROLE, rootOrganization.getName(), currUser));
}
catch (NullPointerException e) {
_log.error("Cannot find root organziation, please check gcube-data.properties file in $CATALINA_HOME/conf folder");
return false;
}
}
/**
*
* @param rolename
* @param organizationName
* @param user
* @return
* @throws SystemException
*/
private boolean hasRole(String rolename, String organizationName, User user) throws SystemException {
for (Role role : user.getRoles())
if (role.getName().compareTo(rolename) == 0 )
return true;
return false;
}
}