diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java index f6f2d2fd6f..4b86a99210 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java @@ -47,6 +47,7 @@ import org.alfresco.repo.template.ISO8601DateFormatMethod; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.JSONtoFmModel; +import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; @@ -119,9 +120,11 @@ public abstract class FeedTaskProcessor Configuration cfg = getFreemarkerConfiguration(ctx); + // local caches for this run of activity posts Map> activityTemplates = new HashMap>(10); - Map> siteConnectedUsers = new TreeMap>(); - Map> followers = new TreeMap>(); + Map> siteConnectedUsers = new HashMap>(); // site -> site members + Map, Set> followerConnectedUsers = new HashMap, Set>(); // user -> followers + Map, Boolean> canUserReadSite = new HashMap, Boolean>(); // -> true/false (note: used when following, implied as true for site members) Map> userFeedControls = new HashMap>(); Map templateCache = new TreeMap(); @@ -258,60 +261,18 @@ public abstract class FeedTaskProcessor model.put("xmldate", new ISO8601DateFormatMethod()); model.put("repoEndPoint", ctx.getRepoEndPoint()); - // Recipients of this post - Set recipients = new HashSet(); - - // Add site members to recipient list - if (thisSite.length() > 0) - { - // Get the members of this site - save hammering the repository by reusing cached site members - Set connectedUsers = siteConnectedUsers.get(thisSite); - if (connectedUsers == null) - { - try - { - // Repository callback to get site members - connectedUsers = getSiteMembers(ctx, thisSite, tenantDomain); - connectedUsers.add(""); // add empty posting userid - to represent site feed ! - } - catch(Exception e) - { - logger.error("Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e); - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - continue; - } - - // Cache them for future use in this same invocation - siteConnectedUsers.put(thisSite, connectedUsers); - } - - recipients.addAll(connectedUsers); - } - - // Add followers to recipient list - - // MT Share - mangle key to be within context of tenant - String key = getTenantKey(activityPost.getUserId(), tenantDomain); - Set followerUsers = followers.get(key); - if (followerUsers == null) + // Get recipients of this post + Set recipients = null; + try { - try - { - followerUsers = getFollowers(activityPost.getUserId(), tenantDomain); - } - catch(Exception e) - { - logger.error("Skipping activity post " + activityPost.getId() + " since failed to get followers: " + e); - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - continue; - } - - followers.put(key, followerUsers); + recipients = getRecipients(ctx, thisSite, activityPost.getUserId(), tenantDomain, siteConnectedUsers, followerConnectedUsers, canUserReadSite); + } + catch (Exception e) + { + logger.error("Skipping activity post " + activityPost.getId() + " since failed to get recipients: " + e); + updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); + continue; } - recipients.addAll(followerUsers); - - // Add the originator to recipients - recipients.add(activityPost.getUserId()); try { @@ -345,7 +306,7 @@ public abstract class FeedTaskProcessor } else { - // read permission check + // node read permission check (if nodeRef is present) if (! canRead(ctx, recipient, model)) { excludedConnections++; @@ -449,7 +410,89 @@ public abstract class FeedTaskProcessor logger.info(sb.toString()); } } - + + private Set getRecipients(RepoCtx ctx, String siteId, String postUserId, String tenantDomain, + Map> siteConnectedUsers, Map, Set> followerConnectedUsers, Map, Boolean> canUserReadSite) throws Exception + { + // Recipients of this post + Set recipients = new HashSet(); + + // Add site members to recipient list + if (siteId.length() > 0) + { + // Get the members of this site - save hammering the repository by reusing cached site members + Set connectedUsers = siteConnectedUsers.get(siteId); + if (connectedUsers == null) + { + try + { + // Repository callback to get site members + connectedUsers = getSiteMembers(ctx, siteId, tenantDomain); + connectedUsers.add(""); // add empty posting userid - to represent site feed ! + } + catch(Exception e) + { + throw new Exception("Failed to get site members: "+e); + } + + // Cache them for future use (across activity posts handled) by this same invocation + siteConnectedUsers.put(siteId, connectedUsers); + } + + recipients.addAll(connectedUsers); + } + + // Add followers to recipient list + + // MT Share + Pair userTenantKey = new Pair(postUserId, tenantDomain); + Set followerUsers = followerConnectedUsers.get(userTenantKey); + if (followerUsers == null) + { + try + { + followerUsers = getFollowers(postUserId, tenantDomain); + } + catch(Exception e) + { + throw new Exception("Failed to get followers: "+e); + } + + // Cache them for future use (across activity posts handled) by this same invocation + followerConnectedUsers.put(userTenantKey, followerUsers); + } + + if (siteId.length() > 0) + { + for (String followerUser : followerUsers) + { + Pair userSiteKey = new Pair(followerUser, siteId); + Boolean canRead = canUserReadSite.get(userSiteKey); + if (canRead == null) + { + // site read permission check (note: only check followers since implied for site members) + canRead = canReadSite(ctx, siteId, followerUser, tenantDomain); + canUserReadSite.put(userSiteKey, canRead); + } + + if (canRead) + { + recipients.add(followerUser); + } + } + } + else + { + recipients.addAll(followerUsers); + } + + + // Add the originator to recipients + recipients.add(postUserId); + + return recipients; + } + public abstract void startTransaction() throws SQLException; public abstract void commitTransaction() throws SQLException; @@ -530,11 +573,6 @@ public abstract class FeedTaskProcessor return TenantService.DEFAULT_DOMAIN; } - private static String getTenantKey(String name, String tenantDomain) - { - return tenantDomain + "." + name; - } - protected Set getSiteMembers(RepoCtx ctx, String siteId, String tenantDomain) throws Exception { // note: tenant domain ignored her - it should already be part of the siteId @@ -567,14 +605,10 @@ public abstract class FeedTaskProcessor return members; } - protected abstract Set getFollowers(String userId, String tenantDomain) throws Exception; + protected abstract boolean canReadSite(final RepoCtx ctx, String siteIdIn, String connectedUser, final String tenantDomain) throws Exception; + protected abstract boolean canRead(RepoCtx ctx, final String connectedUser, Map model) throws Exception; - protected boolean canRead(RepoCtx ctx, final String connectedUser, Map model) throws Exception - { - throw new UnsupportedOperationException("FeedTaskProcessor: Remote callback for 'canRead' not implemented"); - } - protected Map> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception { StringBuffer sbUrl = new StringBuffer(); diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java index 695ec56c9b..ba30196c3b 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java @@ -267,49 +267,70 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); } } + + @Override + protected boolean canReadSite(final RepoCtx ctx, String siteIdIn, String connectedUser, final String tenantDomain) throws Exception + { + if (useRemoteCallbacks) + { + // note: not implemented + throw new UnsupportedOperationException("Not implemented"); + } + + final String siteId = tenantService.getBaseName(siteIdIn, true); + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + return (siteService.getSite(siteId) != null); + } + }, connectedUser); + } protected boolean canRead(RepoCtx ctx, final String connectedUser, Map model) throws Exception { if (useRemoteCallbacks) { // note: not implemented - return super.canRead(ctx, connectedUser, model); + throw new UnsupportedOperationException("Not implemented"); + } + + if (permissionService == null) + { + // if permission service not configured then fallback (ie. no read permission check) + return true; + } + + String nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF); + if (nodeRefStr == null) + { + nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF_PARENT); + } + + if (nodeRefStr != null) + { + final NodeRef nodeRef = new NodeRef(nodeRefStr); + + // MT share + String tenantDomain = (String)model.get(PostLookup.JSON_TENANT_DOMAIN); + if (tenantDomain == null) { tenantDomain = TenantService.DEFAULT_DOMAIN; } + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + return canReadImpl(connectedUser, nodeRef); + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); } else { - if (permissionService == null) - { - // if permission service not configured then fallback (ie. no read permission check) - return true; - } - - String nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF); - if (nodeRefStr == null) - { - nodeRefStr = (String) model.get(PostLookup.JSON_NODEREF_PARENT); - } - - if (nodeRefStr != null) - { - final NodeRef nodeRef = new NodeRef(nodeRefStr); - - // MT share - String tenantDomain = (String)model.get(PostLookup.JSON_TENANT_DOMAIN); - if (tenantDomain == null) { tenantDomain = TenantService.DEFAULT_DOMAIN; } - - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Boolean doWork() throws Exception - { - return canReadImpl(connectedUser, nodeRef); - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - } - + // no nodeRef return true; } } - + private boolean canReadImpl(final String connectedUser, final NodeRef nodeRef) throws Exception { // check for read permission @@ -498,6 +519,12 @@ public class LocalFeedTaskProcessor extends FeedTaskProcessor implements Applica protected Set getFollowers(final String userId, String tenantDomain) throws Exception { + if (useRemoteCallbacks) + { + // note: not implemented + throw new UnsupportedOperationException("Not implemented"); + } + final Set result = new HashSet(); if (subscriptionService.isActive())