package fr.itldev.koya.activities.feed;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.repo.activities.ActivityType;
import org.alfresco.repo.activities.feed.RepoCtx;
import org.alfresco.repo.activities.feed.local.LocalFeedTaskProcessor;
import org.alfresco.repo.domain.activities.ActivityFeedEntity;
import org.alfresco.repo.domain.activities.ActivityPostEntity;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.site.SiteDoesNotExistException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.site.SiteService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import fr.itldev.koya.alfservice.KoyaMailService;
import fr.itldev.koya.alfservice.KoyaNodeService;
import fr.itldev.koya.alfservice.UserService;
import fr.itldev.koya.alfservice.security.SpaceAclService;
import fr.itldev.koya.model.KoyaActivityType;
import fr.itldev.koya.model.KoyaNode;
import fr.itldev.koya.model.impl.Dossier;
import fr.itldev.koya.model.impl.Space;
import fr.itldev.koya.model.impl.User;
import fr.itldev.koya.model.permissions.KoyaPermission;
import fr.itldev.koya.model.permissions.KoyaPermissionCollaborator;
import fr.itldev.koya.model.permissions.KoyaPermissionConsumer;
import fr.itldev.koya.model.permissions.SitePermission;
/**
* Koya Local Feed task processor
*
*
*
*
*/
public class KoyaLocalFeedTaskProcessor extends LocalFeedTaskProcessor {
private static final Log logger = LogFactory
.getLog(KoyaLocalFeedTaskProcessor.class);
private final static Pattern MEMBERUSERNAME_PATTERN = Pattern
.compile(".*memberUserName\\\":\\\"([^\\\"]+)\\\".*");
private SiteService siteService;
private KoyaMailService koyaMailService;
private KoyaNodeService koyaNodeService;
private NodeService nodeService;
private UserService userService;
private SpaceAclService spaceAclService;
@Override
public void setSiteService(SiteService siteService) {
super.setSiteService(siteService);
this.siteService = siteService;
}
@Override
public void setNodeService(NodeService nodeService) {
super.setNodeService(nodeService);
this.nodeService = nodeService;
}
public void setKoyaNodeService(KoyaNodeService koyaNodeService) {
this.koyaNodeService = koyaNodeService;
}
public void setKoyaMailService(KoyaMailService koyaMailService) {
this.koyaMailService = koyaMailService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setSpaceAclService(SpaceAclService spaceAclService) {
this.spaceAclService = spaceAclService;
}
public void process(int jobTaskNode, long minSeq, long maxSeq, RepoCtx ctx)
throws Exception {
long startTime = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
logger.debug("Process: jobTaskNode '" + jobTaskNode
+ "' from seq '" + minSeq + "' to seq '" + maxSeq
+ "' on this node from grid job.");
}
ActivityPostEntity selector = new ActivityPostEntity();
selector.setJobTaskNode(jobTaskNode);
selector.setMinId(minSeq);
selector.setMaxId(maxSeq);
selector.setAppTool(KoyaActivityType.KOYA_APPTOOL);
selector.setStatus(ActivityPostEntity.STATUS.POSTED.toString());
List<ActivityPostEntity> activityPosts = null;
int totalGenerated = 0;
try {
activityPosts = selectPosts(selector);
if (logger.isDebugEnabled()) {
logger.debug("Process: " + activityPosts.size()
+ " activity posts");
}
// for each activity post ...
for (final ActivityPostEntity activityPost : activityPosts) {
// Get recipients of this post
Set<String> recipients = getRecipients(activityPost);
if (logger.isDebugEnabled()) {
logger.debug("Activity " + activityPost.getActivityType()
+ ">>> " + recipients);
}
String activitySummary = null;
// allows JSON to simply pass straight through
activitySummary = activityPost.getActivityData();
/**
* get user joined recipient email from username and add it to
* activitySummary
*/
if (activityPost.getActivityType().equals(
ActivityType.SITE_USER_JOINED)) {
activitySummary = addMemberEmailForCompanyMemberShipActivities(
activitySummary, activityPost.getUserId());
}
if (activityPost.getActivityType().equals(
ActivityType.SITE_USER_REMOVED)) {
try {
// extract user removed userName from activitySummary
Matcher m = MEMBERUSERNAME_PATTERN
.matcher(activitySummary);
if (m.find()) {
activitySummary = addMemberEmailForCompanyMemberShipActivities(
activitySummary, m.group(1));
}
} catch (Exception e) {
// silently continue
}
}
try {
startTransaction();
for (String recipient : recipients) {
ActivityFeedEntity feed = new ActivityFeedEntity();
// Generate activity feed summary
feed.setFeedUserId(recipient);
feed.setPostUserId(activityPost.getUserId());
feed.setActivityType(activityPost.getActivityType());
feed.setActivitySummary(activitySummary);
feed.setSiteNetwork(activityPost.getSiteNetwork());
feed.setAppTool(activityPost.getAppTool());
feed.setPostDate(activityPost.getPostDate());
feed.setPostId(activityPost.getId());
feed.setFeedDate(new Date());
// Insert activity feed
insertFeedEntry(feed);
totalGenerated++;
}
updatePostStatus(activityPost.getId(),
ActivityPostEntity.STATUS.PROCESSED);
commitTransaction();
if (logger.isDebugEnabled()) {
logger.debug("Processed: " + recipients.size()
+ " connections for activity post "
+ activityPost.getId() + ")");
}
} finally {
endTransaction();
}
// TODO do it in a new transaction
// Send email alerts
if (Arrays.asList(SHARING_ACTIVITIES).contains(
activityPost.getActivityType())) {
// TODO Alert for company users
sendShareNotificationMail(activityPost);
}
if ((activityPost.getActivityType().equals(KoyaActivityType.KOYA_CONSUMERUPLOAD))) {
// TODO Alert for company users
sendConsumerUploadNotificationMail(activityPost,recipients);
}
// Send mail for ansync dl file available
if ((activityPost.getActivityType().equals(KoyaActivityType.KOYA_DLFILEAVAILABLE))) {
sendDlNotificationMail(activityPost);
}
}
} catch (SQLException se) {
logger.error(se);
throw se;
} finally {
int postCnt = activityPosts == null ? 0 : activityPosts.size();
// TODO i18n info message
StringBuilder sb = new StringBuilder();
sb.append("Generated ").append(totalGenerated)
.append(" activity feed entr")
.append(totalGenerated == 1 ? "y" : "ies");
sb.append(" for ").append(postCnt).append(" activity post")
.append(postCnt != 1 ? "s" : "").append(" (in ")
.append(System.currentTimeMillis() - startTime)
.append(" msecs)");
logger.info(sb.toString());
}
}
private String addMemberEmailForCompanyMemberShipActivities(
String activitySummary, final String memberUserName) {
User u = AuthenticationUtil
.runAsSystem(new AuthenticationUtil.RunAsWork<User>() {
@Override
public User doWork() throws Exception {
return userService.getUserByUsername(memberUserName);
}
});
// add user email to activitySummary
if (activitySummary.endsWith("}")) {
activitySummary = activitySummary.substring(0,
activitySummary.length() - 1)
+ ",\"memberEmail\":\"" + u.getEmail() + "\"}";
}
return activitySummary;
}
private void sendShareNotificationMail(final ActivityPostEntity activityPost) {
// external user sharing notification mail
if (listCompanyMembers(activityPost.getSiteNetwork(),
SitePermission.CONSUMER).contains(activityPost.getUserId())) {
AuthenticationUtil
.runAsSystem(new AuthenticationUtil.RunAsWork<Void>() {
@Override
public Void doWork() throws Exception {
Map<String, Object> activityPostData = null;
try {
activityPostData = new ObjectMapper()
.readValue(
activityPost.getActivityData(),
Map.class);
NodeRef spaceNodeRef = new NodeRef(
activityPostData.get("spaceNodeRef")
.toString());
// TODO inviter parameter
koyaMailService.sendShareAlertMail(
activityPost.getUserId(), null,
spaceNodeRef);
} catch (Exception e) {
logger.warn("Failed to send share alert mail : "
+ e.toString());
}
return null;
}
});
}
}
private void sendConsumerUploadNotificationMail(final ActivityPostEntity activityPost,final Set<String> recipients) {
AuthenticationUtil
.runAsSystem(new AuthenticationUtil.RunAsWork<Void>() {
@SuppressWarnings("unchecked")
@Override
public Void doWork() throws Exception {
Map<String, Object> activityPostData = null;
try {
activityPostData = new ObjectMapper()
.readValue(
activityPost.getActivityData(),
Map.class);
NodeRef spaceNodeRef = new NodeRef(
activityPostData.get("spaceNodeRef")
.toString());
NodeRef docNodeRef = new NodeRef(
activityPostData.get("nodeRef")
.toString());
if (recipients.isEmpty()) {
logger.warn("No responsible for client document add. Dossier " + spaceNodeRef.toString());
}else{
koyaMailService.sendClientUploadAlertMail(recipients,activityPost.getUserId(),docNodeRef,spaceNodeRef);
}
} catch (Exception e) {
logger.warn("Failed to send consumer upload alert mail : "
+ e.toString());
}
return null;
}
});
}
private void sendDlNotificationMail(final ActivityPostEntity activityPost) {
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>() {
@Override
public Void doWork() throws Exception {
Map<String, Object> activityPostData = null;
try {
activityPostData = new ObjectMapper().readValue(activityPost.getActivityData(),
Map.class);
NodeRef dlFileNodeRef = new NodeRef(activityPostData.get("nodeRef").toString());
String fileName = activityPostData.get("fileName").toString();
koyaMailService.sendDlFileAvailableAlertMail(activityPost.getUserId(),
dlFileNodeRef,fileName);
} catch (Exception e) {
logger.warn("Failed to send dl File Available alert mail : " + e.toString());
}
return null;
}
});
}
private static String[] SHARING_ACTIVITIES = {
KoyaActivityType.KOYA_SPACESHARED,
KoyaActivityType.KOYA_SPACEUNUNSHARED };
private static String[] FILEFOLDER_ACTIVITIES = { ActivityType.FILE_ADDED,
ActivityType.FOLDER_ADDED, ActivityType.FILES_ADDED,
ActivityType.FOLDERS_ADDED, ActivityType.FILE_DELETED,
ActivityType.FOLDER_DELETED, ActivityType.FILE_UPDATED,
KoyaActivityType.FOLDER_UPDATED };
private static String[] COMPANYMEMBERSHIP_ACTIVITIES = {
ActivityType.SITE_USER_JOINED, ActivityType.SITE_USER_REMOVED };
/**
*
* Activity user filering method
*
* TODO process all
*
*
*/
private Set<String> getRecipients(final ActivityPostEntity activityPost) {
NodeRef spaceNodeRef;
/**
* Get nodeRef from activityPost.getActivityData()
*/
// ObjectMapper activityPostData = ;
Map<String, Object> activityPostData = null;
try {
activityPostData = new ObjectMapper().readValue(
activityPost.getActivityData(), Map.class);
} catch (IOException e) {
}
/**
* Select users for Space Sharing Activities syndication
*/
if (Arrays.asList(SHARING_ACTIVITIES).contains(
activityPost.getActivityType())) {
spaceNodeRef = new NodeRef(activityPostData.get("spaceNodeRef")
.toString());
// return list of members or responsibles of space
// + user share destination whatever his role
List<KoyaPermission> rolesSelector = new ArrayList<KoyaPermission>();
rolesSelector.add(KoyaPermissionCollaborator.RESPONSIBLE);
rolesSelector.add(KoyaPermissionCollaborator.MEMBER);
Set<String> users = spaceAclService.listUsersAuthorities(
spaceNodeRef, rolesSelector);
users.add(activityPost.getUserId());
return users;
}
/**
* Select users for Files and Folders Activities syndication
*
*/
if (Arrays.asList(FILEFOLDER_ACTIVITIES).contains(
activityPost.getActivityType())) {
return getFileFolderActivityRecipents(activityPostData,
activityPost);
}
/**
* Select users for Company Membership Activities syndication
*
*/
if (Arrays.asList(COMPANYMEMBERSHIP_ACTIVITIES).contains(
activityPost.getActivityType())) {
// exclude import user
if (!activityPost.getUserId().equals(
activityPost.getSiteNetwork() + "_import")) {
return listCompanyMembers(activityPost.getSiteNetwork(),
SitePermission.MANAGER);
} else {
return new HashSet<>();
}
}
/**
* Select user for Download File Available Activities syndication
*/
if(activityPost.getActivityType().equals(KoyaActivityType.KOYA_DLFILEAVAILABLE)){
HashSet<String> userNotified = new HashSet<>();
userNotified.add(activityPost.getUserId()) ;
return userNotified;
}
/**
* Select users for Space public upload Activities syndication
*
*/
if(activityPost.getActivityType().equals(KoyaActivityType.KOYA_CONSUMERUPLOAD)){
spaceNodeRef = new NodeRef(activityPostData.get("spaceNodeRef")
.toString());
List<KoyaPermission> rolesSelector = new ArrayList<KoyaPermission>();
rolesSelector.add(KoyaPermissionCollaborator.RESPONSIBLE);
rolesSelector.add(KoyaPermissionCollaborator.MEMBER);
Set<String> users = spaceAclService.listUsersAuthorities(
spaceNodeRef, rolesSelector);
/**
* TODO strategy for empty notifiable users list.
*/
return users;
}
logger.warn("Unhandled Activity type : "
+ activityPost.getActivityType());
return new HashSet<>();
}
private Set<String> getFileFolderActivityRecipents(
final Map<String, Object> activityPostData,
ActivityPostEntity activityPost) {
final String[] nodesCandidatesKeys = { "nodeRef", "parentNodeRef" };
Space s = AuthenticationUtil
.runAsSystem(new AuthenticationUtil.RunAsWork<Space>() {
@Override
public Space doWork() throws Exception {
for (String nodeRefCandidate : nodesCandidatesKeys) {
try {
NodeRef candidate = new NodeRef(
activityPostData.get(nodeRefCandidate)
.toString());
if (nodeService.exists(candidate)) {
KoyaNode kn = koyaNodeService
.getKoyaNode(candidate);
if (kn.getClass().isAssignableFrom(
Dossier.class)) {
return (Space) kn;
}
return koyaNodeService
.getFirstParentOfType(candidate,
Dossier.class);
}
} catch (Exception e) {
}
}
return null;
}
});
if (s != null) {
if (activityPost.getActivityType().equals(
ActivityType.FOLDER_DELETED)
&& s.getClass().equals(Space.class)) {
/**
* Folder deleted is a DOssier >
*/
// return list of site manager
// return listCompanyMembers(activityPost.getSiteNetwork(),
// SitePermission.MANAGER);
// TODO return list of responsibles or members on delete node
return new HashSet<String>();
} else {
List<KoyaPermission> rolesSelector = new ArrayList<KoyaPermission>();
rolesSelector.add(KoyaPermissionCollaborator.RESPONSIBLE);
rolesSelector.add(KoyaPermissionCollaborator.MEMBER);
rolesSelector.add(KoyaPermissionConsumer.CLIENT);
return spaceAclService.listUsersAuthorities(s.getNodeRef(),
rolesSelector);
}
}
return new HashSet<String>();
}
public Set<String> listCompanyMembers(final String companyName,
final SitePermission permission) {
return AuthenticationUtil
.runAsSystem(new AuthenticationUtil.RunAsWork<Set<String>>() {
@Override
public Set<String> doWork() throws Exception {
try {
return siteService.listMembers(companyName, "",
permission.toString(), -1).keySet();
} catch (SiteDoesNotExistException ex) {
// silently catch site doesn't exists exception
return new HashSet<>();
}
}
});
}
}