package org.kalipo.service; import org.kalipo.config.Constants; import org.kalipo.domain.*; import org.kalipo.domain.Thread; import org.kalipo.repository.*; import org.kalipo.service.util.Asserts; import org.kalipo.web.rest.KalipoException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.thymeleaf.context.IContext; import org.thymeleaf.spring4.SpringTemplateEngine; import javax.inject.Inject; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This service is used to write notification messages about likes (LIKE), mentions in a comment (MENTION), replies (REPLY), 3rd party comment deletion (DELETION), reports of comments to mods and supermods (REPORT), REVIEW, APPROVAL */ @Service public class NotificationService { private static final int PAGE_SIZE = 20; private static final Pattern FIND_MENTIONED_USER = Pattern.compile("@([a-z0-9]+)"); //todo fix pattern to not match email addresses private final Logger log = LoggerFactory.getLogger(NotificationService.class); @Inject private NotificationRepository notificationRepository; @Inject private ThreadRepository threadRepository; @Inject private SiteRepository siteRepository; @Inject private CommentRepository commentRepository; @Inject private UserRepository userRepository; @Inject private MailService mailService; @Inject private NonceService nonceService; @Inject private SpringTemplateEngine templateEngine; @Inject private Environment environment; public Page<Notification> findByUserWithPages(final String userId, final int pageNumber) throws KalipoException { Asserts.isNotNull(userId, "userId"); Asserts.isCurrentLogin(userId); PageRequest pageable = new PageRequest(pageNumber, PAGE_SIZE, Sort.Direction.DESC, Constants.PARAM_CREATED_DATE); return notificationRepository.findByRecipientId(userId, pageable); } // -- ASYNCHRONOUS CALLS ------------------------------------------------------------------------------------------- @Async public void notifyMentionedUsers(Comment comment, String initiatorId) { try { Asserts.isNotNull(comment, "comment"); // find mentioned usernames, starting with @ like @myname Matcher matcher = FIND_MENTIONED_USER.matcher(comment.getBody()); Set<String> uqLogins = new HashSet<String>(); while (matcher.find()) { String login = matcher.group(1); uqLogins.add(login); } for (String login : uqLogins) { // notify @login sendNotice(login, initiatorId, Notification.Type.MENTION, comment.getId()); } } catch (Exception e) { log.error(String.format("Unable to notify mentioned user. Reason: %s", e.getMessage())); } } @Async public void notifyAuthorOfParent(Comment comment, String initiatorId) { try { Asserts.isNotNull(comment, "comment"); if (comment.getParentId() != null) { Comment parent = commentRepository.findOne(comment.getParentId()); if (parent != null) { sendNotice(parent.getAuthorId(), initiatorId, Notification.Type.REPLY, comment.getId()); } } } catch (Exception e) { log.error(String.format("Unable to notify author of parent of %s. Reason: %s", comment, e.getMessage())); } } @Async public void notifyAsync(String recipientId, String initiatorId, Notification.Type type, String commentId) { try { Asserts.isNotNull(recipientId, "recipientId"); Asserts.isNotNull(type, "type"); Asserts.isNotNull(commentId, "commentId"); sendNotice(recipientId, initiatorId, type, commentId, null); } catch (Exception e) { log.error(String.format("Unable to notify %s with %s of %s. Reason: %s", recipientId, type, commentId, e.getMessage())); } } @Async public void notifySuperModsOfFraudulentComment(Comment comment, String initiatorId) { try { // todo impl } catch (Exception e) { log.error(String.format("Unable to notify superMods of fraud-comment %s. Reason: %s", comment, e.getMessage())); } } @Async public void announcePendingReport(String threadId, Report report) { try { Asserts.isNotNull(threadId, "threadId"); Asserts.isNotNull(report, "report"); Thread thread = threadRepository.findOne(threadId); Asserts.isNotNull(thread, "threadId"); Site site = siteRepository.findOne(thread.getSiteId()); Locale locale = Locale.ENGLISH; String subject = String.format("Pending Report in '%s'", thread.getTitle()); Comment comment = commentRepository.findOne(report.getCommentId()); for(String modId : site.getModeratorIds()) { User mod = userRepository.findOne(modId); String content = createPendingReportEmailFromTemplate(mod, report, comment, locale); mailService.sendEmail(mod.getEmail(), subject, content, false, true); } } catch (Exception e) { log.error(String.format("Unable to notify mods in thread %s with report %s. Reason: %s", threadId, report, e.getMessage())); } } @Async public void announcePendingComment(Thread thread, Comment comment) { try { Asserts.isNotNull(thread, "thread"); Asserts.isNotNull(comment, "comment"); Site site = siteRepository.findOne(thread.getSiteId()); Locale locale = Locale.ENGLISH; String subject = String.format("Pending Comment in '%s'", thread.getTitle()); for(String modId : site.getModeratorIds()) { User mod = userRepository.findOne(modId); String content = createPendingCommentEmailFromTemplate(mod, comment, locale); mailService.sendEmail(mod.getEmail(), subject, content, false, true); } } catch (Exception e) { log.error(String.format("Unable to notify mods (site %s) of comment %s. Reason: %s", thread.getSiteId(), comment, e.getMessage())); } } @Async public void announceCommentRejected(Comment comment) { } @Async public void announceBan(String userId, String initiatorId) { } @Async public void announceCommentDeleted(Comment comment) { } @Async public void announceReportRejected(Report report) { } @Async public void announceReportApproved(Report report) { } @Async public void announceCommentLiked(Comment comment) { } // -- private void sendNotice(String recipientId, String initiatorId, Notification.Type type, String resourceId, String message) { log.debug(String.format("-> notify %s of %s on resource %s", recipientId, type.name(), resourceId)); Notification notification = new Notification(); notification.setMessage(message); notification.setRecipientId(recipientId); notification.setInitiatorId(initiatorId); notification.setResourceId(resourceId); notification.setType(type); notificationRepository.save(notification); } private void sendNotice(String recipientId, String initiatorId, Notification.Type type, String resourceId) { sendNotice(recipientId, initiatorId, type, resourceId, null); } protected String createPendingReportEmailFromTemplate(User user, final Report report, Comment comment, Locale locale) { Map<String, Object> variables = new HashMap<>(); variables.put("displayName", user.getDisplayName()); variables.put("baseUrl", environment.getProperty("baseUrl")); variables.put("report", report); variables.put("reason", String.format("%s %s", report.getReason(), report.getCustomReason())); variables.put("nonce", nonceService.createNonce()); variables.put("comment", comment); IContext context = new org.thymeleaf.context.Context(locale, variables); return templateEngine.process("pendingReportEmail", context); } protected String createPendingCommentEmailFromTemplate(User user, final Comment comment, Locale locale) { Map<String, Object> variables = new HashMap<>(); variables.put("displayName", user.getDisplayName()); variables.put("baseUrl", environment.getProperty("baseUrl")); variables.put("nonce", nonceService.createNonce()); variables.put("comment", comment); IContext context = new org.thymeleaf.context.Context(locale, variables); return templateEngine.process("pendingCommentEmail", context); } }