diff --git a/distro/changelog.xml b/distro/changelog.xml index 6bff387..4ae3bb0 100644 --- a/distro/changelog.xml +++ b/distro/changelog.xml @@ -1,4 +1,8 @@ + + Added support for teply via email in Messages + Fix for #577 diff --git a/pom.xml b/pom.xml index 8f560f5..4ca358b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,8 +11,8 @@ org.gcube.portal social-mail-servlet war - 1.1.0-SNAPSHOT - + 1.2.0-SNAPSHOT + social-mail-servlet Webapp This component read periodically email replies from user wanting to reply to a post via email and exports users in LDAP @@ -66,10 +66,20 @@ social-networking-library provided - + org.gcube.portal notifications-common-library - [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT) + provided + + + org.gcube.common + home-library + provided + + + org.gcube.common + home-library-jcr + provided com.google diff --git a/src/main/java/org/gcube/portal/socialmail/PeriodicTask.java b/src/main/java/org/gcube/portal/socialmail/PeriodicTask.java index 8b98335..c9f5081 100644 --- a/src/main/java/org/gcube/portal/socialmail/PeriodicTask.java +++ b/src/main/java/org/gcube/portal/socialmail/PeriodicTask.java @@ -3,6 +3,7 @@ package org.gcube.portal.socialmail; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Properties; import java.util.UUID; @@ -24,7 +25,16 @@ import org.gcube.application.framework.core.session.SessionManager; import org.gcube.application.framework.core.util.GenderType; import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager; import org.gcube.applicationsupportlayer.social.NotificationsManager; +import org.gcube.applicationsupportlayer.social.mailing.AppType; import org.gcube.applicationsupportlayer.social.mailing.EmailPlugin; +import org.gcube.common.homelibrary.home.HomeLibrary; +import org.gcube.common.homelibrary.home.exceptions.HomeNotFoundException; +import org.gcube.common.homelibrary.home.exceptions.InternalErrorException; +import org.gcube.common.homelibrary.home.workspace.Workspace; +import org.gcube.common.homelibrary.home.workspace.exceptions.ItemNotFoundException; +import org.gcube.common.homelibrary.home.workspace.exceptions.WorkspaceFolderNotFoundException; +import org.gcube.common.homelibrary.home.workspace.sharing.WorkspaceMessage; +import org.gcube.common.homelibrary.home.workspace.sharing.WorkspaceMessageManager; import org.gcube.common.portal.PortalContext; import org.gcube.portal.custom.communitymanager.OrganizationsUtil; import org.gcube.portal.databook.server.DatabookStore; @@ -35,8 +45,10 @@ 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.portal.notifications.bean.GenericItemBean; import org.gcube.portal.notifications.thread.CommentNotificationsThread; import org.gcube.portal.notifications.thread.LikeNotificationsThread; +import org.gcube.portal.notifications.thread.MessageNotificationsThread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -154,38 +166,26 @@ public class PeriodicTask implements Runnable { String subject = message.getSubject(); _log.info("Parsing email of " + message.getFrom()[0] + " with subject: " + subject); if (isValidReply(message)) { - String feedId = extractFeedId(message); - String commentText = extractText(portalName, feedId, message); - _log.info("Extracted id: " + feedId + " text=" + commentText); - String escapedCommentText = Utils.escapeHtmlAndTransformUrl(commentText); - + String subAddressField = extractSubaddress(message); Address[] froms = message.getFrom(); String emailAddress = froms == null ? null : ((InternetAddress) froms[0]).getAddress(); - ASLSession fakeSession = getFakeASLSession(emailAddress); - if (fakeSession != null) { - if (escapedCommentText.trim().compareTo("") == 0) {//it is a favorite/subscription - _log.debug("Found favorite/subscription for feed with subject: " + subject); - favoriteFeed(feedId, fakeSession); - } - else { - Comment comment = new Comment(UUID.randomUUID().toString(), fakeSession.getUsername(), - new Date(), feedId, escapedCommentText, fakeSession.getUserFullName(), fakeSession.getUserAvatarId()); - _log.debug("The EscapedCommentText =>" + escapedCommentText); - boolean commentCommitResult = false; - try { - if (socialStore.addComment(comment)) - commentCommitResult = true; - } catch (FeedIDNotFoundException e) { - _log.error("Related post not found for this comment " + e.getMessage()); - e.printStackTrace(); - } - if (commentCommitResult) { //the notifications should start - notifyUsersInvolved(comment, escapedCommentText, feedId, fakeSession); - } + if (fakeSession != null) { + if (subAddressField.endsWith(AppType.POST.toString()) || subAddressField.endsWith(AppType.POST.toString().toLowerCase())) { //it is a post, a comment on a post or a mention + _log.debug("Looks like a post, a comment on a post or a mention to me"); + String feedId = extractIdentifier(subAddressField); + handlePostReply(portalName, feedId, message, fakeSession); + } + else if (subAddressField.endsWith(AppType.MSG.toString()) || subAddressField.endsWith(AppType.MSG.toString().toLowerCase())) { //it is a message + _log.debug("Looks like a message reply to me"); + String messageId = extractIdentifier(subAddressField); + handleMessageReply(portalName, messageId, message, fakeSession); + } else { + _log.warn("cannot identify the type of this email reply from " + message.getFrom()[0] + " with subject: " + subject); } - } else { + } + else { _log.warn("User Not Recognized, going to discard Message from emailAddress = " + emailAddress); } } @@ -209,6 +209,125 @@ public class PeriodicTask implements Runnable { e.printStackTrace(); } } + + + /** + * this method manages the replies coming from message notifications + * @param portalName + * @param messageId the message identifier in the System managing the messages (currently in the Home Library) + * @param message the javax mail Message instance + * @param fakeSession + * @throws Exception + */ + private void handleMessageReply(String portalName, String messageId, Message message, ASLSession fakeSession) { + String subject = ""; + String messageText = ""; + String escapedMessageText = ""; + try { + subject = message.getSubject(); + messageText = extractText(portalName, messageId, message); + escapedMessageText = Utils.escapeHtmlAndTransformUrl(messageText); + } catch (Exception e1) { + e1.printStackTrace(); + } + + _log.debug("Found message reply, subject: " + subject, " body: " + messageText); + String newMessageId = null; + Workspace workspace; + List recipientIds = null; + try { + workspace = getWorkspace(fakeSession); + + WorkspaceMessageManager messageManager = workspace.getWorkspaceMessageManager(); + WorkspaceMessage theMessage = messageManager.getReceivedMessage(messageId); + recipientIds = theMessage.getAddresses(); + //add the sender and remove the person who is replying from the recipients + String sender = theMessage.getSender().getPortalLogin(); + recipientIds.add(sender); + recipientIds.remove(fakeSession.getUsername()); + + _log.debug("Message Recipients:"); + for (String rec : recipientIds) { + _log.debug(rec); + } + + _log.debug("Trying to send message with subject: " + subject, " to: " + recipientIds.toString()); + newMessageId = messageManager.sendMessageToPortalLogins(subject, messageText, new ArrayList(), recipientIds); + + } catch (WorkspaceFolderNotFoundException | InternalErrorException | HomeNotFoundException | ItemNotFoundException e) { + e.printStackTrace(); + } + + _log.debug("Message with subject: " + subject, " hase been sent, returned id: " + newMessageId); + + if (newMessageId != null) { + _log.debug("Sending message notifications ... "); + List recipients = getUsersbyUserId(recipientIds); + NotificationsManager nm = new ApplicationNotificationsManager(fakeSession); + Thread thread = new Thread(new MessageNotificationsThread(recipients, messageId, subject, escapedMessageText, nm)); + thread.start(); + } else { + _log.debug("Could not send message reply"); + } + + } + /** + * return the User instance given his id + * @param recipientIds + * @return + */ + private List getUsersbyUserId(List recipientIds) { + List recipients = new ArrayList(); + for (String userid : recipientIds) { + com.liferay.portal.model.User user; + try { + user = UserLocalServiceUtil.getUserByScreenName(OrganizationsUtil.getCompany().getCompanyId(), userid); + recipients.add(new GenericItemBean(""+user.getUserId(), user.getScreenName(), user.getFullName(), "")); + } catch (PortalException | SystemException e) { + e.printStackTrace(); + } + } + return recipients; + } + + /** + * this method manages the replies coming from post notifications + * @param portalName + * @param feedId the identifier in the System managing the feeds + * @param message the javax mail Message instance + * @param fakeSession + * @throws Exception + */ + private void handlePostReply(String portalName, String feedId, Message message, ASLSession fakeSession) throws Exception { + String commentText = extractText(portalName, feedId, message); + _log.info("Extracted id: " + feedId + " text=" + commentText); + String escapedCommentText = Utils.escapeHtmlAndTransformUrl(commentText); + String subject = message.getSubject(); + + if (escapedCommentText.trim().compareTo("") == 0) {//it is a favorite/subscription + _log.debug("Found favorite/subscription for feed with subject: " + subject); + favoriteFeed(feedId, fakeSession); + } + else { + Comment comment = new Comment(UUID.randomUUID().toString(), fakeSession.getUsername(), + new Date(), feedId, escapedCommentText, fakeSession.getUserFullName(), fakeSession.getUserAvatarId()); + + _log.debug("The EscapedCommentText =>" + escapedCommentText); + boolean commentCommitResult = false; + try { + if (socialStore.addComment(comment)) + commentCommitResult = true; + } catch (FeedIDNotFoundException e) { + _log.error("Related post not found for this comment " + e.getMessage()); + e.printStackTrace(); + } + if (commentCommitResult) { //the notifications should start + notifyUsersInvolved(comment, escapedCommentText, feedId, fakeSession); + } + } + + } + /** * favorite the feed to subscribe to further comments * @param feedId @@ -291,12 +410,12 @@ public class PeriodicTask implements Runnable { return toReturn; } /** - * + * extracts the sub-address (the part after the + and before the @) from the email addressee * @param message - * @return + * @return the sub-address without + and @ * @throws MessagingException */ - private static String extractFeedId(Message message) throws MessagingException { + private static String extractSubaddress(Message message) throws MessagingException { Address[] emails = message.getRecipients(RecipientType.TO); String toParse = emails[0].toString(); int plus = toParse.indexOf('+'); @@ -305,6 +424,20 @@ public class PeriodicTask implements Runnable { return null; return toParse.substring(plus+1, at); } + /** + * extracts the identifier (the part before the $) from the Subaddress + * @param message + * @return the identifier + * @throws MessagingException + */ + private static String extractIdentifier(String subAddress) { + String id = subAddress; //for backward compatibility + int at = subAddress.indexOf('$'); + if (at > -1) + id = subAddress.substring(0, at); + return id; + } + /** * the email is considered a valid reply if and only of it contains the EmailPlugin.WRITE_ABOVE_TO_REPLY text in the body * @param message the message to check @@ -330,12 +463,16 @@ public class PeriodicTask implements Runnable { } String[] lines = toParse.split(System.getProperty("line.separator")); for (int i = 0; i < lines.length; i++) { - if (lines[i].contains(SEPARATOR)) + if (lines[i].contains(SEPARATOR)) { + _log.debug("Yes, it is a valid Reply"); return true; + } } + _log.debug("NOT a valid Reply"); return false; } catch (Exception e) { e.printStackTrace(); + _log.error("Exceptiom returning NOT a valid Reply"); return false; } } @@ -360,9 +497,11 @@ public class PeriodicTask implements Runnable { toParse = part.getContent().toString(); } else { - _log.debug("Found g text/plain Message, getting text"); + _log.debug("Found text/plain Message, getting text"); toParse = messageContent.toString(); } + _log.debug("Got Message content = " + toParse); + //cut the text below the WRITE_ABOVE_TO_REPLY text String[] lines = toParse.split(System.getProperty("line.separator")); int until = -1; @@ -385,6 +524,22 @@ public class PeriodicTask implements Runnable { if (! (i == until -1) ) sb.append("\n"); } - return sb.toString().trim(); + + String toReturn = sb.toString().trim(); + _log.debug("Returning text extracted = " + toReturn); + return toReturn; } + + /** + * + * @return the workspace instance + * @throws InternalErrorException + * @throws HomeNotFoundException + * @throws WorkspaceFolderNotFoundException + */ + private Workspace getWorkspace(ASLSession session) throws InternalErrorException, HomeNotFoundException, WorkspaceFolderNotFoundException { + Workspace workspace = HomeLibrary.getUserWorkspace(session.getUsername()); + return workspace; + } + }