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 portraitIdCache; public void init() { store = new DBCassandraAstyanaxImpl(); portraitIdCache = new HashMap(); } 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 getAllUpdateUserFeeds() { String userName = getASLSession().getUsername(); ArrayList toMerge = new ArrayList(); HashMap feedsMap = new HashMap(); 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 OrganizationFeeds = (ArrayList) store.getRecentFeedsByVRE(vreid, 10); for (Feed feed : OrganizationFeeds) { feedsMap.put(feed.getKey(), feed); } } } //User Own Feeds ArrayList userFeeds = (ArrayList) store.getRecentFeedsByUser(userName, 10); for (Feed feed : userFeeds) { feedsMap.put(feed.getKey(), feed); } //UserFriends Feeds ArrayList userFriendsIds = (ArrayList)store.getFriends(userName); for (String userid : userFriendsIds) { for (Feed feed : store.getRecentFeedsByUser(userid, 10)) { feedsMap.put(feed.getKey(), feed); } } //Portal Feeds ArrayList portalFeeds = (ArrayList) store.getAllPortalPrivacyLevelFeeds(); for (Feed feed : portalFeeds) { feedsMap.put(feed.getKey(), feed); } for (String key: feedsMap.keySet()) { toMerge.add(feedsMap.get(key)); } } ArrayList toReturn = new ArrayList(); //return only 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 getOnlyConnectionsUserFeeds() { ArrayList toMerge = new ArrayList(); HashMap feedsMap = new HashMap(); String userName = getASLSession().getUsername(); try { if (! withinPortal) { return getEclipseResult(userName, true); } else { //UserFriends Feeds ArrayList userFriendsIds = (ArrayList)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 toReturn = new ArrayList(); //return only 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 getEclipseResult(String userName, boolean onlyConnections) throws PrivacyLevelTypeNotFoundException, FeedTypeNotFoundException, ColumnNameNotFoundException, FeedIDNotFoundException { ArrayList toMerge = new ArrayList(); HashMap feedsMap = new HashMap(); ArrayList OrganizationFeeds = (ArrayList) store.getRecentFeedsByVRE("/gcube/devsec/devVRE", 10); for (Feed feed : OrganizationFeeds) { feedsMap.put(feed.getKey(), feed); } if (! onlyConnections) { //User Own Feeds ArrayList userFeeds = (ArrayList) store.getRecentFeedsByUser(userName, 10); for (Feed feed : userFeeds) feedsMap.put(feed.getKey(), feed); // //Portal Feeds ArrayList portalFeeds = (ArrayList) store.getAllPortalPrivacyLevelFeeds(); for (Feed feed : portalFeeds) feedsMap.put(feed.getKey(), feed); } //UserFriends Feeds ArrayList userFriendsIds = (ArrayList)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 getOnlyMyUserFeeds() { String userName = getASLSession().getUsername(); _log.trace("getOnly UserFeeds for " + userName); ArrayList userFeeds = null; try { userFeeds = (ArrayList) store.getRecentFeedsByUser(userName, 15); } catch (Exception e) { e.printStackTrace(); } return enhanceFeeds(userFeeds); } @Override public ArrayList getOnlyLikedFeeds() { String userName = getASLSession().getUsername(); _log.trace("getLiked Feeds for " + userName); ArrayList userFeeds = null; try { userFeeds = (ArrayList) 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("&", "&"); return toReturn; } /** * this method sorts the Feeds in Chronological Reversed order and adds additional user informations * @param toEnhance * @return */ private ArrayList enhanceFeeds(ArrayList toEnhance) { ArrayList toReturn = new ArrayList(); String username = getASLSession().getUsername(); //sort the Feeds Collections.sort(toEnhance, Collections.reverseOrder()); ArrayList likedFeeds = (ArrayList) 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 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 comments2Attach = new ArrayList(); 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 getAllLikesByFeed(String feedid) { ArrayList toReturn = (ArrayList) store.getAllLikesByFeed(feedid); _log.trace("Asking likes for " + feedid); for (Like like : toReturn) { like.setThumbnailURL(getUserImagePortraitUrlLocal(like.getUserid())); } return toReturn; } @Override public ArrayList getAllCommentsByFeed(String feedid) { _log.trace("Asking comments for " + feedid); ArrayList toReturn = (ArrayList) 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("&", "&").replaceAll("<", "<") .replaceAll(">", ">"); } @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 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; } }