From ed839dcbe0e46b5868e9ab6b5f1ab7f41cd7f24b Mon Sep 17 00:00:00 2001 From: Brian Remmington Date: Sun, 15 Sep 2013 15:00:13 +0000 Subject: [PATCH] Merged BRANCHES/V4.2 to HEAD: 55206: Branch created for 4.2.x releases. 55209: Merged HEAD to BRANCHES/V4.2: 55208: Added Sharepoint config to unit test classpath to fix VtiRequestDispatcherTest. 55222: Merged HEAD-BUG-FIX to V4.2 55220: Merged V4.1-BUG-FIX (4.1.7) to HEAD-BUG-FIX (4.2) 55218: Fix for ALF-19894 Site is not displayed after restoring from Trashcan This bug was reported on 4.2, but the bug is also present on 4.1. I have added a testcase to reproduce the bug along with a fix. The problem was in SiteServiceImpl.delete where the code was writing an empty property value into the {}memberships property and therefore there was no record of which users had been members of the deleted site. 55245: Fixed javadoc while checking that ALF-19055 was fixed. ActivityService no longer uses 'format 55246: CLOUD-2050 -Content changes are not synced from Cloud to On-Premise (errors in the logs) 55249: Hazelcast: Added ability to create cache with 'read-backup-data' via property *.readBackupData 55250: Hazelcast cache builder now accepts 'async-backup-count' value and any error in setting a value logs the full exception 55251: Asynchronous cache: Better logging of in- and after-transaction processes 55252: Asynchronous cache: Better logging of in- and after-transaction processes 55266: Workflow REST API fix for variable retrieval 55276: Merged HEAD to BRANCHES/V4.2: 55274: Disabling Sharepoint unit tests until we can make them run properly in the build. 55278: ALF-19889 - String for Brazilian Portuguese 55279: Merged DEV to 4.2 ALF-17464 : Replication jobs aren't displayed until sorting by some characteristic - Changing not exact equals (!==) to not euqals (!=) 55280: Fix for ALF-19865 - Forgot password link redirects to Login page. Also cleaned up the mess that is the 'Sign in to Alfresco in the cloud' dialog. 55281: Probable fix for ALF-19225 Intermittent test failures in SubscriptionServiceActivitiesTest 55282: ALF-19865 - Cloud Sync profile area now looks like the rest of profile area. 55285: Merged BRANCHES/DEV/BELARUS/HEAD-2013_08_27 to BRANCHES/V4.2: 55068: ALF-19915 : MT and WebDAV: Content is lost when uploading non-empty document 55286: Merged BRANCHES/DEV/BELARUS/HEAD-2013_08_29 to BRANCHES/V4.2: 55207: ALF-19915 : MT and WebDAV: Content is lost when uploading non-empty document 55210: ALF-19915 : MT and WebDAV: Content is lost when uploading non-empty document 55297: As part of prep work for fixing ALF-20023 Recent Sites and Favorite Sites in copy/move pickers empty, 55299: ALF-19556: IE10 specific fix for file upload browse (impressive how IE finds more and more innovative ways to spoil things for itself) 55300: Fixed date issue when creating a new process instance 55308: Resync to HEAD@55302 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@55309 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../AbstractAsynchronouslyRefreshedCache.java | 3 +- .../cache/AbstractRefreshableCacheEvent.java | 45 +- .../repo/cache/RefreshableCacheEvent.java | 2 - .../org/alfresco/repo/lock/LockUtils.java | 23 +- .../alfresco/repo/site/SiteServiceImpl.java | 43 +- .../cmr/activities/ActivityService.java | 6 - .../preference/PreferenceServiceImplTest.java | 334 ++++++---- .../repo/site/SiteServiceImplMoreTest.java | 134 +++- .../SubscriptionServiceActivitiesTest.java | 625 +++++++++++------- .../script/test_preferenceService.js | 16 +- 10 files changed, 815 insertions(+), 416 deletions(-) diff --git a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java index 3594c7d2b8..e9e91ceb9b 100644 --- a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java +++ b/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java @@ -585,6 +585,8 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr /** * Build the cache entry for the specific tenant. + * This method is called in a thread-safe manner i.e. it is only ever called by a single + * thread. */ protected abstract T buildCache(String tenantId); @@ -690,7 +692,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - @SuppressWarnings("deprecation") @Override public void flush() { diff --git a/source/java/org/alfresco/repo/cache/AbstractRefreshableCacheEvent.java b/source/java/org/alfresco/repo/cache/AbstractRefreshableCacheEvent.java index 735fc10e4e..4761c78f19 100644 --- a/source/java/org/alfresco/repo/cache/AbstractRefreshableCacheEvent.java +++ b/source/java/org/alfresco/repo/cache/AbstractRefreshableCacheEvent.java @@ -25,13 +25,9 @@ package org.alfresco.repo.cache; */ public abstract class AbstractRefreshableCacheEvent implements RefreshableCacheEvent { - /** - * - */ private static final long serialVersionUID = 1324638640132648062L; private String cacheId; - private String tenantId; AbstractRefreshableCacheEvent(String cacheId, String tenantId) @@ -40,34 +36,51 @@ public abstract class AbstractRefreshableCacheEvent implements RefreshableCacheE this.tenantId = tenantId; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.RefreshableCacheEvent#getCacheId() - */ @Override public String getCacheId() { return cacheId; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.RefreshableCacheEvent#getTenantId() - */ @Override public String getTenantId() { return tenantId; } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return "AbstractRefreshableCacheEvent [cacheId=" + cacheId + ", tenantId=" + tenantId + "]"; } - + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((cacheId == null) ? 0 : cacheId.hashCode()); + result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + AbstractRefreshableCacheEvent other = (AbstractRefreshableCacheEvent) obj; + if (cacheId == null) + { + if (other.cacheId != null) return false; + } + else if (!cacheId.equals(other.cacheId)) return false; + if (tenantId == null) + { + if (other.tenantId != null) return false; + } + else if (!tenantId.equals(other.tenantId)) return false; + return true; + } } diff --git a/source/java/org/alfresco/repo/cache/RefreshableCacheEvent.java b/source/java/org/alfresco/repo/cache/RefreshableCacheEvent.java index a742c40115..193e42a6bd 100644 --- a/source/java/org/alfresco/repo/cache/RefreshableCacheEvent.java +++ b/source/java/org/alfresco/repo/cache/RefreshableCacheEvent.java @@ -30,13 +30,11 @@ public interface RefreshableCacheEvent extends Serializable { /** * Get the cache id - * @return */ public String getCacheId(); /** * Get the affected tenant id - * @return */ public String getTenantId(); } diff --git a/source/java/org/alfresco/repo/lock/LockUtils.java b/source/java/org/alfresco/repo/lock/LockUtils.java index f93b46f8e0..2da4a852ed 100644 --- a/source/java/org/alfresco/repo/lock/LockUtils.java +++ b/source/java/org/alfresco/repo/lock/LockUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -25,6 +25,25 @@ import org.alfresco.service.cmr.repository.NodeRef; public class LockUtils { + /** + * Indicates if the node is locked AND the current user is not lock owner. + * + * @param nodeRef node reference + * @param lockService LockService + */ + public static boolean isLockedAndNotLockOwner(NodeRef nodeRef, LockService lockService) + { + LockStatus lockStatus = lockService.getLockStatus(nodeRef); + switch (lockStatus) + { + case NO_LOCK: + case LOCK_EXPIRED: + case LOCK_OWNER: + return false; + default: + return true; + } + } /** * Indicates if the node is locked AND it's not a WRITE_LOCK for the current user.

@@ -43,7 +62,7 @@ public class LockUtils case LOCK_EXPIRED: return false; case LOCK_OWNER: - return lockService.getLockType(nodeRef) != LockType.WRITE_LOCK; + return lockService.getLockType(nodeRef).equals(LockType.WRITE_LOCK); default: return true; } diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 1c3f4d2fc6..fde22ca01e 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -1454,6 +1454,9 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic */ public void deleteSite(final String shortName) { + // In deleting the site node, we have to jump through a few hoops to manage the site groups. + // The order of execution is important here. + logger.debug("delete site :" + shortName); final NodeRef siteNodeRef = getSiteNodeRef(shortName); if (siteNodeRef == null) @@ -1465,11 +1468,35 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // Delete the cached reference siteNodeRefCache.remove(shortName); - // Collection for recording the group memberships present on the site - final Map> groupsMemberships = new HashMap>(); - - // Save the group memberships so we can use them later - this.nodeService.setProperty(siteNodeRef, QName.createQName(null, "memberships"), (Serializable)groupsMemberships); + // Get and retain the membership of the site we're deleting. We do this to support restoration of a site node from the trashcan. + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Void doWork() throws Exception + { + final String siteGroup = getSiteGroup(shortName, true); + if (authorityService.authorityExists(siteGroup)) + { + // Collection for recording the group memberships present on the site + final Map> groupsMemberships = new HashMap>(); + + // Iterate over the role related groups and delete then + Set permissions = permissionService.getSettablePermissions(siteType); + for (String permission : permissions) + { + String siteRoleGroup = getSiteRoleGroup(shortName, permission, true); + + // Collect up the memberships so we can potentially restore them later + Set groupUsers = authorityService.getContainedAuthorities(null, siteRoleGroup, true); + groupsMemberships.put(siteRoleGroup, groupUsers); + } + + // Save the group memberships so we can use them later + nodeService.setProperty(siteNodeRef, QName.createQName(null, "memberships"), (Serializable)groupsMemberships); + } + + return null; + } + }, AuthenticationUtil.getSystemUserName()); // The default behaviour is that sites cannot be deleted. But we disable that behaviour here // in order to allow site deletion only via this service. Share calls this service for deletion. @@ -1493,7 +1520,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic // Delete the associated groups AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { - public Object doWork() throws Exception + public Void doWork() throws Exception { // Delete the master site group final String siteGroup = getSiteGroup(shortName, true); @@ -1507,10 +1534,6 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic { String siteRoleGroup = getSiteRoleGroup(shortName, permission, true); - // Collect up the memberships so we can potentially restore them later - Set groupUsers = authorityService.getContainedAuthorities(null, siteRoleGroup, true); - groupsMemberships.put(siteRoleGroup, groupUsers); - // Delete the site role group authorityService.deleteAuthority(siteRoleGroup); } diff --git a/source/java/org/alfresco/service/cmr/activities/ActivityService.java b/source/java/org/alfresco/service/cmr/activities/ActivityService.java index f6323c2c6a..aae6e27311 100644 --- a/source/java/org/alfresco/service/cmr/activities/ActivityService.java +++ b/source/java/org/alfresco/service/cmr/activities/ActivityService.java @@ -42,7 +42,6 @@ public interface ActivityService extends ActivityPostService * Will return activities for all users across all sites, or optionally for all users for specified site. * * @param userId - required - * @param format - required * @param siteId - optional, if set then will filter by given siteId else return all sites * @return list of JSON feed entries */ @@ -61,7 +60,6 @@ public interface ActivityService extends ActivityPostService * note: if both excludes are true then no activities will be returned. * * @param userId - required - * @param format - required * @param siteId - optional, if set then will filter by given siteId else return all sites * @param excludeThisUser - if TRUE then will exclude activities for this user (hence returning other users only) * @param excludeOthersUsers - if TRUE then will exclude activities for other users (hence returning this user only) @@ -83,7 +81,6 @@ public interface ActivityService extends ActivityPostService * note: if both excludes are true then no activities will be returned. * * @param userId - required - * @param format - required * @param siteId - optional, if set then will filter by given siteId else return all sites * @param excludeThisUser - if TRUE then will exclude activities for this user (hence returning other users only) * @param excludeOthersUsers - if TRUE then will exclude activities for other users (hence returning this user only) @@ -106,7 +103,6 @@ public interface ActivityService extends ActivityPostService * note: if both excludes are true then no activities will be returned. * * @param userId - required - * @param format - required * @param siteId - optional, if set then will filter by given siteId else return all sites * @param excludeThisUser - if TRUE then will exclude activities for this user (hence returning other users only) * @param excludeOthersUsers - if TRUE then will exclude activities for other users (hence returning this user only) @@ -129,7 +125,6 @@ public interface ActivityService extends ActivityPostService * note: if both excludes are true then no activities will be returned. * * @param userId - required - * @param format - required * @param siteId - optional, if set then will filter by given siteId else return all sites * @param excludeThisUser - if TRUE then will exclude activities for this user (hence returning other users only) * @param excludeOthersUsers - if TRUE then will exclude activities for other users (hence returning this user only) @@ -149,7 +144,6 @@ public interface ActivityService extends ActivityPostService * Retrieve site feed * * @param activityType - required - * @param format - required * @return list of JSON feed entries */ @NotAuditable diff --git a/source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java b/source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java index 49e54265e5..b04129aac0 100644 --- a/source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,10 @@ */ package org.alfresco.repo.preference; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + import java.io.Serializable; import java.util.Date; import java.util.HashMap; @@ -25,181 +29,217 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.jscript.ClasspathScriptLocation; -import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.util.BaseAlfrescoSpringTest; -import org.alfresco.util.TestWithUserUtils; +import org.alfresco.util.test.junitrules.AlfrescoPerson; +import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; +import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule.RunAsUser; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; /** - * Thumbnail service implementation unit test + * {@link PreferenceService} implementation unit test * * @author Roy Wetherall + * @author Neil Mc Erlean (refactoring to JUnit Rules and enabling disabled tests) */ -public class PreferenceServiceImplTest extends BaseAlfrescoSpringTest +public class PreferenceServiceImplTest { - private static final String USER_ONE = "userOne"; + private static final Log log = LogFactory.getLog(PreferenceServiceImplTest.class); - private static final String USER_BAD = "userBad"; + // JUnit rule to initialise the default Alfresco spring configuration + @ClassRule public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit(); + + private static final String USERNAME2 = "userBad"; + + // Rules to create test users. Note that this class is unusual in that we do *NOT* want to reuse users across test methods. + public AlfrescoPerson testUser1 = new AlfrescoPerson(APP_CONTEXT_INIT); + public AlfrescoPerson testUser2 = new AlfrescoPerson(APP_CONTEXT_INIT, USERNAME2); + + // A rule to have all test methods be run as "UserOne". + public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(testUser1); + + // Tie them together in a Rule Chain + @Rule public RuleChain ruleChain = RuleChain.outerRule(testUser1) + .around(testUser2) + .around(runAsRule); + - private ScriptService scriptService; - - private NodeService nodeService; - - private AuthenticationComponent authenticationComponent; - - private PreferenceService preferenceService; - - private PersonService personService; - - private ContentService contentService; - - /** - * Called during the transaction setup - */ - protected void onSetUpInTransaction() throws Exception + // Various services + private static ContentService CONTENT_SERVICE; + private static PersonService PERSON_SERVICE; + private static PreferenceService PREFERENCE_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + private static ScriptService SCRIPT_SERVICE; + + private static NodeRef COMPANY_HOME; + + @BeforeClass public static void initStaticData() throws Exception { - super.onSetUpInTransaction(); + CONTENT_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("ContentService", ContentService.class); + PERSON_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PersonService", PersonService.class); + PREFERENCE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PreferenceService", PreferenceService.class); + SCRIPT_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("ScriptService", ScriptService.class); + TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + + Repository repositoryHelper = (Repository) APP_CONTEXT_INIT.getApplicationContext().getBean("repositoryHelper"); + COMPANY_HOME = repositoryHelper.getCompanyHome(); + } + + @Test public void testPreferences() throws Exception + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + // Try and get preferences before they have been set + Map prefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername()); + assertNotNull(prefs); + assertEquals(0, prefs.size()); - // Get the required services - this.scriptService = (ScriptService) this.applicationContext.getBean("ScriptService"); - this.nodeService = (NodeService) this.applicationContext.getBean("NodeService"); - this.authenticationComponent = (AuthenticationComponent) this.applicationContext - .getBean("authenticationComponent"); - this.preferenceService = (PreferenceService) this.applicationContext.getBean("PreferenceService"); - this.personService = (PersonService) this.applicationContext.getBean("PersonService"); - this.contentService = (ContentService) this.applicationContext.getBean("ContentService"); + // Lets set some preferences for the user + prefs = new HashMap(5); + prefs.put("alfresco.one.alpha", "string"); + prefs.put("alfresco.one.beta", 100); + prefs.put("alfresco.two.alpha", 3.142); + prefs.put("alfresco.two.beta", COMPANY_HOME); + prefs.put("alfresco.two.gamma", new Date()); + prefs.put("atTheRoot", "thisIsAtTheRoot"); + PREFERENCE_SERVICE.setPreferences(testUser1.getUsername(), prefs); - // Do the test's as userOne - TestWithUserUtils.authenticateUser(USER_ONE, "PWD", this.authenticationService, this.authenticationComponent); + NodeRef personNodeRef = PERSON_SERVICE.getPerson(testUser1.getUsername()); + ContentReader reader = CONTENT_SERVICE.getReader(personNodeRef, ContentModel.PROP_PREFERENCE_VALUES); + log.debug("JSON: \n" + prettyJson(reader.getContentString())); + + // Try and get all the preferences + prefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername(), null); + assertNotNull(prefs); + assertEquals(6, prefs.size()); + + // Try and get some of the preferences + prefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername(), "alfresco.two"); + assertNotNull(prefs); + assertEquals(3, prefs.size()); + + // Clear some of the preferences + PREFERENCE_SERVICE.clearPreferences(testUser1.getUsername(), "alfresco.two"); + prefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername(), null); + assertNotNull(prefs); + assertEquals(3, prefs.size()); + + // Clear all the preferences + PREFERENCE_SERVICE.clearPreferences(testUser1.getUsername()); + prefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername()); + assertNotNull(prefs); + assertEquals(0, prefs.size()); + return null; + } + }); } - public void testPreferences() throws Exception + @Test(expected=AccessDeniedException.class) + @RunAsUser(userName=USERNAME2) + public void testBadUser() { - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); - - // Try and get preferences before they have been set - Map prefs = this.preferenceService.getPreferences(USER_ONE); - assertNotNull(prefs); - assertEquals(0, prefs.size()); - - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); - - // Lets set some preferences for the user - prefs = new HashMap(5); - prefs.put("alfresco.one.alpha", "string"); - prefs.put("alfresco.one.beta", 100); - prefs.put("alfresco.two.alpha", 3.142); - prefs.put("alfresco.two.beta", this.rootNodeRef); - prefs.put("alfresco.two.gamma", new Date()); - prefs.put("atTheRoot", "thisIsAtTheRoot"); - this.preferenceService.setPreferences(USER_ONE, prefs); - - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); - - NodeRef personNodeRef = this.personService.getPerson(USER_ONE); - ContentReader reader = this.contentService.getReader(personNodeRef, ContentModel.PROP_PREFERENCE_VALUES); - System.out.println("JSON: " + reader.getContentString()); - - // Try and get all the preferences - prefs = this.preferenceService.getPreferences(USER_ONE, null); - assertNotNull(prefs); - assertEquals(6, prefs.size()); - - // Try and get some of the preferences - prefs = this.preferenceService.getPreferences(USER_ONE, "alfresco.two"); - assertNotNull(prefs); - assertEquals(3, prefs.size()); - - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); - - // Clear some of the preferences - this.preferenceService.clearPreferences(USER_ONE, "alfresco.two"); - prefs = this.preferenceService.getPreferences(USER_ONE, null); - assertNotNull(prefs); - assertEquals(3, prefs.size()); - - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); - - // Clear all the preferences - this.preferenceService.clearPreferences(USER_ONE); - prefs = this.preferenceService.getPreferences(USER_ONE); - assertNotNull(prefs); - assertEquals(0, prefs.size()); - - // assertEquals(USER_ONE, AuthenticationUtil.getCurrentUserName()); + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + Map prefs = new HashMap(5); + prefs.put("alfresco.one.alpha", "string"); + PREFERENCE_SERVICE.setPreferences(testUser1.getUsername(), prefs); + + return null; + } + }); } - public void xtestBadUser() + @Test public void testGetOtherUserPreferences() { - assertEquals(USER_ONE, authenticationComponent.getCurrentUserName()); - - try + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { - // Lets set some preferences for the user - Map prefs = new HashMap(5); - prefs.put("alfresco.one.alpha", "string"); - prefs.put("alfresco.one.beta", 100); - prefs.put("alfresco.two.alpha", 3.142); - prefs.put("alfresco.two.beta", this.rootNodeRef); - prefs.put("alfresco.two.gamma", new Date()); - prefs.put("atTheRoot", "thisIsAtTheRoot"); - this.preferenceService.setPreferences(USER_BAD, prefs); - - fail("This should have raised an exception since we are trying to update preferences that are not our own!"); - } - catch (Exception exception) + @Override public Void execute() throws Throwable + { + // Lets set some preferences for the user one + Map prefs = new HashMap(5); + prefs.put("alfresco.one.alpha", "string"); + prefs.put("alfresco.one.beta", 100); + PREFERENCE_SERVICE.setPreferences(testUser1.getUsername(), prefs); + + Map userOnePrefs = PREFERENCE_SERVICE.getPreferences(testUser1.getUsername()); + assertNotNull(userOnePrefs); + assertEquals(2, prefs.size()); + return null; + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { - // this is OK :) - } - - } - - public void testGetOtherUserPreferences() - { - assertEquals(USER_ONE, authenticationComponent.getCurrentUserName()); - - // Lets set some preferences for the user one - Map prefs = new HashMap(5); - prefs.put("alfresco.one.alpha", "string"); - prefs.put("alfresco.one.beta", 100); - this.preferenceService.setPreferences(USER_ONE, prefs); - - Map userOnePrefs = this.preferenceService.getPreferences(USER_ONE); - assertNotNull(userOnePrefs); - assertEquals(2, prefs.size()); - - // login as USER_BAD - TestWithUserUtils.authenticateUser(USER_BAD, "PWD", this.authenticationService, this.authenticationComponent); - assertEquals(USER_BAD, authenticationComponent.getCurrentUserName()); - - try - { - // Lets USER_BAD tries and get USER_ONE's preferences - @SuppressWarnings("unused") - Map badUserPrefs = this.preferenceService.getPreferences(USER_ONE); - fail("This should have raised an exception since we are trying to get preferences that are not our own!"); - } - catch (Exception exception) - { - // this is OK - } + @Override public Void execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(USERNAME2); + // This should not be possible + try + { + PREFERENCE_SERVICE.getPreferences(testUser1.getUsername()); + } + catch (AccessDeniedException expected) { return null; } + fail("Expected exception when trying to access another user's prefs"); + + return null; + } + }); } // == Test the JavaScript API == - - public void testJSAPI() throws Exception + @Test public void testJSAPI() throws Exception { - // assertEquals(USER_ONE, authenticationComponent.getCurrentUserName()); - - ScriptLocation location = new ClasspathScriptLocation( - "org/alfresco/repo/preference/script/test_preferenceService.js"); - this.scriptService.executeScript(location, new HashMap(0)); + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + // This test is running as user1 and the JavaScript needs to know that. + Map model = new HashMap(); + model.put("username", testUser1.getUsername()); + + ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/preference/script/test_preferenceService.js"); + SCRIPT_SERVICE.executeScript(location, model); + + return null; + } + }); + } + + private String prettyJson(String jsonString) + { + String result = jsonString; + try + { + JSONObject json = new JSONObject(new JSONTokener(jsonString)); + result = json.toString(2); + } catch (JSONException ignored) + { + // Intentionally empty + } + return result; } } diff --git a/source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java b/source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java index 1fe1bbf40e..1decfa42e3 100644 --- a/source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java +++ b/source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -19,17 +19,26 @@ package org.alfresco.repo.site; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.Map; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.node.archive.NodeArchiveService; +import org.alfresco.repo.node.archive.RestoreNodeReport; +import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; @@ -41,11 +50,14 @@ import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; import org.alfresco.util.test.junitrules.TemporarySites; import org.alfresco.util.test.junitrules.TemporarySites.TestSiteAndMemberInfo; import org.alfresco.util.test.junitrules.TemporarySitesTest; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; +import org.junit.rules.TestName; import org.springframework.extensions.webscripts.GUID; /** @@ -59,6 +71,8 @@ import org.springframework.extensions.webscripts.GUID; */ public class SiteServiceImplMoreTest { + protected static Log log = LogFactory.getLog(SiteServiceImplMoreTest.class); + // Rule to initialise the default Alfresco spring configuration public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides("classpath:sites/test-" + TemporarySitesTest.class.getSimpleName() + "-context.xml"); @@ -71,15 +85,21 @@ public class SiteServiceImplMoreTest .around(STATIC_TEST_SITES); public RunAsFullyAuthenticatedRule runAllTestsAsAdmin = new RunAsFullyAuthenticatedRule(AuthenticationUtil.getAdminUserName()); + public TemporarySites perMethodTestSites = new TemporarySites(APP_CONTEXT_INIT); public AlfrescoPerson testUser = new AlfrescoPerson(APP_CONTEXT_INIT); // Need to ensure the rules are in the correct order so that we're authenticated as admin before trying to create the person. @Rule public RuleChain ruleChain = RuleChain.outerRule(runAllTestsAsAdmin) + .around(perMethodTestSites) .around(testUser); + @Rule public TestName testName = new TestName(); // Various services + private static AuthorityService AUTHORITY_SERVICE; private static NamespaceService NAMESPACE_SERVICE; + private static NodeService NODE_SERVICE; + private static NodeArchiveService NODE_ARCHIVE_SERVICE; private static SiteService SITE_SERVICE; private static RetryingTransactionHelper TRANSACTION_HELPER; @@ -88,7 +108,10 @@ public class SiteServiceImplMoreTest @BeforeClass public static void initStaticData() throws Exception { + AUTHORITY_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("AuthorityService", AuthorityService.class); NAMESPACE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("namespaceService", NamespaceService.class); + NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("NodeService", NodeService.class); + NODE_ARCHIVE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("nodeArchiveService", NodeArchiveService.class); SITE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("siteService", SiteService.class); TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); @@ -158,4 +181,113 @@ public class SiteServiceImplMoreTest } }); } + + /** + * This test ensures that when sites are deleted (moved to the trashcan) and then restored, that the 4 role-based groups are + * restored correctly and that any users who were members of those groups are made members once more. + * + * @see SiteServiceImpl#deleteSite(String) + * @see SiteServiceImpl#onRestoreNode(org.alfresco.service.cmr.repository.ChildAssociationRef) + */ + @Test public void deleteSiteAndRestoreEnsuringSiteGroupsAreRecovered() throws Exception + { + // Implementation note: as of Alfresco 4.2, authorities cannot be archived and are always hard-deleted. + // Therefore, on site restore, the SiteService *recreates* the deleted groups associated with the restored site. + // Having recreated them, it then needs to add any old members (users, TODO what about groups?) into those newly created site groups. + // It does this by writing the site members and their roles onto a property on the st:site node in the archive & reading them on restore. + + // Choose a site name that will link back to this test case... + final String siteShortName = testName.getMethodName(); + log.debug("Creating test site called: " + siteShortName); + + // ...and create the site + final TestSiteAndMemberInfo testSiteAndMemberInfo = perMethodTestSites.createTestSiteWithUserPerRole(siteShortName, "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); + + // Now get the various site-related data that we want to examine after deletion & restoration + final Map userNameToRoleMap = + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback>() + { + public Map execute() throws Throwable + { + final Map userNameToRoleMap = new HashMap(); + + // Which users are members of which groups? + for (String role : SITE_SERVICE.getSiteRoles()) + { + // putAll here shouldn't overwrite any keys in the above map as each authority should only have one role in a site. + userNameToRoleMap.putAll(SITE_SERVICE.listMembers(siteShortName, null, role, 0, true)); + } + + // Some sanity checking before we delete the site + final String siteContributorGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, "SiteContributor"); + assertTrue("Site contributor user was not in site contributors group", AUTHORITY_SERVICE.getContainedAuthorities(AuthorityType.USER, siteContributorGroup, true).contains(testSiteAndMemberInfo.siteContributor)); + assertEquals("Site contributor user did not have expected Contributor role", + SiteModel.SITE_CONTRIBUTOR, + userNameToRoleMap.get(testSiteAndMemberInfo.siteContributor)); + + log.debug("About to delete site."); + SITE_SERVICE.deleteSite(siteShortName); + log.debug("Site deleted."); + + return userNameToRoleMap; + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + assertThatArchivedNodeExists(testSiteAndMemberInfo.siteInfo.getNodeRef(), "Site node not found in archive."); + + // At this point we might assert that the groups associated with the site were gone, but that's an implementation detail really. + + log.debug("About to restore site node from archive"); + + final NodeRef archivedSiteNode = NODE_ARCHIVE_SERVICE.getArchivedNode(testSiteAndMemberInfo.siteInfo.getNodeRef()); + RestoreNodeReport report = NODE_ARCHIVE_SERVICE.restoreArchivedNode(archivedSiteNode); + // ...which should work + assertEquals("Failed to restore site from archive", RestoreStatus.SUCCESS, report.getStatus()); + + log.debug("Successfully restored site from arhive."); + + return null; + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // The site itself should have been restored, of course... + assertTrue("The site noderef was not restored as expected", NODE_SERVICE.exists(testSiteAndMemberInfo.siteInfo.getNodeRef())); + + // But the group authority nodes should be restored (recreated) as well. + for (String role : SITE_SERVICE.getSiteRoles()) + { + final String siteGroup = SITE_SERVICE.getSiteRoleGroup(siteShortName, role); + assertTrue("Site group for role " + role + " did not exist after site restoration", + AUTHORITY_SERVICE.authorityExists(siteGroup)); + } + + log.debug(SITE_SERVICE.listMembers(siteShortName, null, null, 0, true).size() + " members..."); + for (Map.Entry entry : SITE_SERVICE.listMembers(siteShortName, null, null, 0, true).entrySet()) { log.debug(entry); } + + // And finally, the original members of the site should have been given the same membership that they had before. + for (Map.Entry entry : userNameToRoleMap.entrySet()) + { + assertEquals("Unexpected role for site user: " + entry.getKey(), + entry.getValue(), + SITE_SERVICE.getMembersRole(siteShortName, entry.getKey())); + } + + return null; + } + }); + } + + private void assertThatArchivedNodeExists(NodeRef originalNodeRef, String failureMsg) + { + final NodeRef archivedNodeRef = NODE_ARCHIVE_SERVICE.getArchivedNode(originalNodeRef); + assertTrue(failureMsg, NODE_SERVICE.exists(archivedNodeRef)); + } } diff --git a/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java b/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java index 88c1b1ba0a..06a818b857 100644 --- a/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java +++ b/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java @@ -18,14 +18,14 @@ */ package org.alfresco.repo.subscriptions; +import static org.junit.Assert.assertEquals; + import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import junit.framework.TestCase; - import org.alfresco.model.ContentModel; import org.alfresco.repo.activities.feed.FeedGenerator; import org.alfresco.repo.activities.feed.local.LocalFeedTaskProcessor; @@ -34,6 +34,8 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.activities.ActivityService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentService; @@ -47,36 +49,85 @@ import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.subscriptions.SubscriptionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; -import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.GUID; -import org.alfresco.util.PropertyMap; +import org.alfresco.util.test.junitrules.AlfrescoPerson; +import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.alfresco.util.test.junitrules.TemporarySites; +import org.apache.commons.lang.mutable.MutableInt; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.springframework.context.ApplicationContext; -public class SubscriptionServiceActivitiesTest extends TestCase +public class SubscriptionServiceActivitiesTest { + private static final Log log = LogFactory.getLog(SubscriptionServiceActivitiesTest.class); + + // JUnit Rule to initialise the Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit(); + + // We'll suffix each user id with a unique number to allow for repeated re-runs of this test class. + // We need to do this as the feed entries for deleted users do not get deleted immediately. + private final static long NOW = System.currentTimeMillis(); + public static final String USER_ONE_NAME = "UserOne" + NOW; + public static final String USER_TWO_NAME = "UserTwo" + NOW; + private static String ADMIN; + + // JUnit Rules to create 2 test users. + public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_ONE_NAME); + public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_TWO_NAME); + + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain STATIC_RULE_CHAIN = RuleChain.outerRule(APP_CONTEXT_INIT) + .around(TEST_USER1) + .around(TEST_USER2); + + // A JUnit Rule to manage test nodes use in each test method + @Rule public TemporarySites testSites = new TemporarySites(APP_CONTEXT_INIT); + // Location of activity type templates (for site activities) // assumes test-resources is on classpath protected static final String TEST_TEMPLATES_LOCATION = "activities"; - protected ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - protected SubscriptionService subscriptionService; - protected PersonService personService; - protected SiteService siteService; - protected ActivityService activityService; - protected NodeService nodeService; - protected ContentService contentService; - protected PostLookup postLookup; - protected FeedGenerator feedGenerator; + protected static SubscriptionService subscriptionService; + protected static PersonService personService; + protected static SiteService siteService; + protected static ActivityService activityService; + protected static NodeService nodeService; + protected static ContentService contentService; + protected static PostLookup postLookup; + protected static FeedGenerator feedGenerator; + protected static RetryingTransactionHelper transactionHelper; - @Override - public void setUp() throws Exception + private static Scheduler QUARTZ_SCHEDULER; + + // Test Sites - these are all created by USER_ONE & hence USER_ONE is the SiteManager. + private SiteInfo publicSite, + privateSite1, privateSite2, + modSite1, modSite2; + + @BeforeClass public static void setUp() throws Exception { + final ApplicationContext ctx = APP_CONTEXT_INIT.getApplicationContext(); + // Let's shut down the scheduler so that we aren't competing with the // scheduled versions of the post lookup and // feed generator jobs - Scheduler scheduler = (Scheduler) ctx.getBean("schedulerFactory"); - scheduler.shutdown(); + // Note that to ensure this test class can be run within a suite, that we do not want to actually 'shutdown' the scheduler. + // It may be needed by other test classes and must be restored to life after this class has run. + QUARTZ_SCHEDULER = ctx.getBean("schedulerFactory", Scheduler.class); + QUARTZ_SCHEDULER.standby(); // Get the required services subscriptionService = (SubscriptionService) ctx.getBean("SubscriptionService"); @@ -85,6 +136,7 @@ public class SubscriptionServiceActivitiesTest extends TestCase activityService = (ActivityService) ctx.getBean("activityService"); nodeService = (NodeService) ctx.getBean("NodeService"); contentService = (ContentService) ctx.getBean("ContentService"); + transactionHelper = (RetryingTransactionHelper) ctx.getBean("retryingTransactionHelper"); ChildApplicationContextFactory activitiesFeed = (ChildApplicationContextFactory) ctx.getBean("ActivitiesFeed"); ApplicationContext activitiesFeedCtx = activitiesFeed.getApplicationContext(); @@ -97,40 +149,29 @@ public class SubscriptionServiceActivitiesTest extends TestCase templateSearchPaths.add(TEST_TEMPLATES_LOCATION); feedProcessor.setTemplateSearchPaths(templateSearchPaths); feedProcessor.setUseRemoteCallbacks(false); + } + + @AfterClass public static void restartQuartzScheduler() throws SchedulerException + { + // We put the scheduler in standby mode BeforeClass. Now we must restore it. + QUARTZ_SCHEDULER.start(); + } + + @Before public void createTestSites() throws Exception + { + ADMIN = AuthenticationUtil.getAdminUserName(); - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - } - - protected void deletePerson(String userId) - { - personService.deletePerson(userId); - } - - protected NodeRef createPerson(String userId) - { - deletePerson(userId); + final String guid = GUID.generate(); - PropertyMap properties = new PropertyMap(5); - properties.put(ContentModel.PROP_USERNAME, userId); - properties.put(ContentModel.PROP_FIRSTNAME, userId); - properties.put(ContentModel.PROP_LASTNAME, "Test"); - properties.put(ContentModel.PROP_EMAIL, userId + "@email.com"); + // admin creates the test sites. This is how this test case was before refactoring. TODO Probably better to have a non-admin user create the sites. + publicSite = testSites.createSite("sitePreset", "pub" + guid, "", "", SiteVisibility.PUBLIC, ADMIN); + privateSite1 = testSites.createSite("sitePreset", "priv1" + guid, "", "", SiteVisibility.PRIVATE, ADMIN); + privateSite2 = testSites.createSite("sitePreset", "priv2" + guid, "", "", SiteVisibility.PRIVATE, ADMIN); + modSite1 = testSites.createSite("sitePreset", "mod1" + guid, "", "", SiteVisibility.MODERATED, ADMIN); + modSite2 = testSites.createSite("sitePreset", "mod2" + guid, "", "", SiteVisibility.MODERATED, ADMIN); + log.debug("Created some test sites..."); - return personService.createPerson(properties); - } - - protected void deleteSite(String siteId) - { - if (siteService.getSite(siteId) != null) - { - siteService.deleteSite(siteId); - } - } - - protected SiteInfo createSite(String siteId, SiteVisibility visibility) - { - deleteSite(siteId); - return siteService.createSite("sitePreset", siteId, null, null, visibility); + // test site cleanup is handled automatically by the JUnit Rule. } protected NodeRef addTextContent(String siteId, String name) @@ -161,7 +202,7 @@ public class SubscriptionServiceActivitiesTest extends TestCase Map titledProps = new HashMap(); titledProps.put(ContentModel.PROP_TITLE, name); titledProps.put(ContentModel.PROP_DESCRIPTION, name); - this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); + nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); @@ -181,189 +222,327 @@ public class SubscriptionServiceActivitiesTest extends TestCase feedGenerator.execute(); } - public void testFollowingActivity() throws Exception + @Test public void testFollowingActivity() throws Exception { - final String userId1 = "bob" + GUID.generate(); - final String userId2 = "tom" + GUID.generate(); + // We'll change things in the system in order to cause the generation of activity events and compare the feeds with these totals as we go. + // Initially, both users have zero activity feed entries. + final MutableInt user1Entries = new MutableInt(0); + final MutableInt user2Entries = new MutableInt(0); + // Java's requirement for final modifiers on variables accessed within inner classes means we can't use simple ints here. - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - createPerson(userId1); - createPerson(userId2); - - createSite(userId1+"pub", SiteVisibility.PUBLIC); - siteService.setMembership(userId1+"pub", userId1, SiteModel.SITE_MANAGER); - - createSite(userId1+"priv1", SiteVisibility.PRIVATE); - siteService.setMembership(userId1+"priv1", userId1, SiteModel.SITE_MANAGER); - - createSite(userId1+"priv2", SiteVisibility.PRIVATE); - siteService.setMembership(userId1+"priv2", userId1, SiteModel.SITE_MANAGER); - - createSite(userId1+"mod1", SiteVisibility.MODERATED); - siteService.setMembership(userId1+"mod1", userId1, SiteModel.SITE_MANAGER); - - createSite(userId1+"mod2", SiteVisibility.MODERATED); - siteService.setMembership(userId1+"mod2", userId1, SiteModel.SITE_MANAGER); - - List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 0, feed.size()); - - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 0, feed.size()); - - // userId1 + 5, userId2 + 0 - generateFeed(); - - feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 5, feed.size()); - - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 0, feed.size()); - - return null; - } - }, AuthenticationUtil.getAdminUserName()); + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + for (SiteInfo s : new SiteInfo[] { publicSite, privateSite1, privateSite2, modSite1, modSite2 }) + { + siteService.setMembership(s.getShortName(), USER_ONE_NAME, SiteModel.SITE_MANAGER); + } + return null; + } + }); + log.debug("Made user '" + USER_ONE_NAME + "' a SiteManager in each test site."); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - subscriptionService.follow(userId1, userId2); - return null; - } - }, userId1); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - subscriptionService.follow(userId2, userId1); - return null; - } - }, userId2); + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + log.debug("Now to check if the activity tables have the correct number of entries for our test users"); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + assertEquals(USER_ONE_NAME + " had wrong feed size.", user1Entries.intValue(), feed.size()); + + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + assertEquals(USER_TWO_NAME + " had wrong feed size.", user2Entries.intValue(), feed.size()); + + generateFeed(); + // User1 now has 5 'user-joined' events. User2 has no events. + user1Entries.add(5); + + feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug(USER_ONE_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_ONE_NAME + " had wrong feed size", user1Entries.intValue(), feed.size()); + + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + log.debug(USER_TWO_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_TWO_NAME + " had wrong feed size", user2Entries.intValue(), feed.size()); + return null; + } + }); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - // userId1 + 5, userId2 + 2 - generateFeed(); - - List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 7, feed.size()); - - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 2, feed.size()); - return null; - } - }, AuthenticationUtil.getAdminUserName()); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - addTextContent(userId1+"pub", userId1+"pub-a"); - addTextContent(userId1+"priv1", userId1+"priv1-a"); - addTextContent(userId1+"priv2", userId1+"priv2-a"); - addTextContent(userId1+"mod1", userId1+"mod1-a"); - addTextContent(userId1+"mod2", userId1+"mod2-a"); - return null; - } - }, userId1); + doWorkAs(USER_ONE_NAME, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + subscriptionService.follow(USER_ONE_NAME, USER_TWO_NAME); + return null; + } + }); + log.debug(USER_ONE_NAME + " is now following " + USER_TWO_NAME); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - // userId1 + 5, userId2 + 1 - generateFeed(); - - List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 12, feed.size()); - - // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 3, feed.size()); - - siteService.setMembership(userId1+"priv2", userId2, SiteModel.SITE_CONSUMER); - siteService.setMembership(userId1+"mod2", userId2, SiteModel.SITE_MANAGER); - - // userId1 + 2, userId2 + 2 - generateFeed(); - - feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 14, feed.size()); - - // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 5, feed.size()); - - return null; - } - }, AuthenticationUtil.getAdminUserName()); - - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - @Override - public Object doWork() throws Exception - { - addTextContent(userId1+"pub", userId1+"pub-b"); - addTextContent(userId1+"priv1", userId1+"priv1-b"); - addTextContent(userId1+"priv2", userId1+"priv2-b"); - addTextContent(userId1+"mod1", userId1+"mod1-b"); - addTextContent(userId1+"mod2", userId1+"mod2-b"); - - return null; - } - }, userId1); - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + doWorkAs(USER_TWO_NAME, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + subscriptionService.follow(USER_TWO_NAME, USER_ONE_NAME); + return null; + } + }); + log.debug("And " + USER_TWO_NAME + " is now following " + USER_ONE_NAME); + + + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + generateFeed(); + // Both users see a new 'followed' event. + user1Entries.add(1); + user2Entries.add(1); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug(USER_ONE_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_ONE_NAME + "'s feed was wrong size", user1Entries.intValue(), feed.size()); + + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + log.debug(USER_TWO_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_TWO_NAME + "'s feed was wrong size", user2Entries.intValue(), feed.size()); + return null; + } + }); + + + doWorkAs(USER_ONE_NAME, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + addTextContent(publicSite.getShortName(), USER_ONE_NAME+"pub-a"); + addTextContent(privateSite1.getShortName(), USER_ONE_NAME+"priv1-a"); + addTextContent(privateSite2.getShortName(), USER_ONE_NAME+"priv2-a"); + addTextContent(modSite1.getShortName(), USER_ONE_NAME+"mod1-a"); + addTextContent(modSite2.getShortName(), USER_ONE_NAME+"mod2-a"); + return null; + } + }); + log.debug(USER_ONE_NAME + " added some content across the sites."); + + + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + generateFeed(); + // User1 sees 5 'file-added' events. + // User2 sees nothing new. + user1Entries.add(5); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug(USER_ONE_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_ONE_NAME + "'s feed was wrong size", user1Entries.intValue(), feed.size()); + + // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + log.debug(USER_TWO_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_TWO_NAME + "'s feed was wrong size", user2Entries.intValue(), feed.size()); + + siteService.setMembership(privateSite2.getShortName(), USER_TWO_NAME, SiteModel.SITE_CONSUMER); + siteService.setMembership(modSite2.getShortName(), USER_TWO_NAME, SiteModel.SITE_MANAGER); + + log.debug(USER_TWO_NAME + "'s role changed on some sites."); + + generateFeed(); + // User1 & 2 both see 2 new 'joined' events. + user1Entries.add(2); + user2Entries.add(2); + + feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug(USER_ONE_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_ONE_NAME + "'s feed was wrong size", user1Entries.intValue(), feed.size()); + + // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + log.debug(USER_TWO_NAME + "'s feed: " + prettyJson(feed)); + assertEquals(USER_TWO_NAME + "'s feed was wrong size", user2Entries.intValue(), feed.size()); + return null; + } + }); + + + doWorkAs(USER_ONE_NAME, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + addTextContent(publicSite.getShortName(), USER_ONE_NAME+"pub-b"); + addTextContent(privateSite1.getShortName(), USER_ONE_NAME+"priv1-b"); + addTextContent(privateSite2.getShortName(), USER_ONE_NAME+"priv2-b"); + addTextContent(modSite1.getShortName(), USER_ONE_NAME+"mod1-b"); + addTextContent(modSite2.getShortName(), USER_ONE_NAME+"mod2-b"); + return null; + } + }); + log.debug(USER_ONE_NAME + " has added some more content..."); + + + + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + generateFeed(); + // User1 sees 5 more 'file-added' events + // User2 only sees 2 of them + user1Entries.add(5); + user2Entries.add(2); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + assertEquals("User's feed was wrong size", user1Entries.intValue(), feed.size()); + + // note: userId2 should not see activities from followers in moderated sites that they do not belong to (ALF-16460) + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + assertEquals("User's feed was wrong size", user2Entries.intValue(), feed.size()); + + return null; + } + }); + + + log.debug("Now to delete the test sites..."); + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + for (SiteInfo s : new SiteInfo[] { publicSite, privateSite1, privateSite2, modSite1, modSite2 }) + { + deleteSite(s.getShortName()); + } + log.debug("Deleted all the test sites."); + + return null; + } + }); + + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + // Feeds should now be reduced to 'follow' events only - see FeedCleaner's behaviours/policies/transaction listeners. + user1Entries.setValue(1); + user2Entries.setValue(1); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug(USER_ONE_NAME + "'s feed:\n" + prettyJson(feed)); + assertEquals("User's feed was wrong size", user1Entries.intValue(), feed.size()); + + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + assertEquals("User's feed was wrong size", user2Entries.intValue(), feed.size()); + + return null; + } + }); + + + log.debug("Now to delete the users..."); + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + for (String user : new String[] { USER_ONE_NAME, USER_TWO_NAME }) + { + deletePerson(user); + } + log.debug("Deleted the test people."); + + return null; + } + }); + + + doWorkAs(ADMIN, new RetryingTransactionCallback() + { + @Override public Void execute() throws Throwable + { + // Now both users should be reduced to having no events. + // FIXME So shouldn't these numbers be down to 0 now? + // Use log4j.logger.org.alfresco.repo.activities.feed.cleanup.FeedCleaner=trace to see the FeedCleaner's work + user1Entries.setValue(1); + user2Entries.setValue(1); + + List feed = activityService.getUserFeedEntries(USER_ONE_NAME, null, false, false, null, null); + log.debug("User1's feed: " + prettyJson(feed)); + + assertEquals("User's feed was wrong size", user1Entries.intValue(), feed.size()); + + feed = activityService.getUserFeedEntries(USER_TWO_NAME, null, false, false, null, null); + assertEquals("User's feed was wrong size", user2Entries.intValue(), feed.size()); + + return null; + } + }); + } + + private void deleteSite(String siteShortName) + { + if (siteService.getSite(siteShortName) != null) { - @Override - public Object doWork() throws Exception + log.debug("Deleting site: " + siteShortName); + siteService.deleteSite(siteShortName); + } + else + { + log.debug("Not deleting site: " + siteShortName + ", as it doesn't appear to exist"); + } + } + + private void deletePerson(String userName) + { + if (personService.personExists(userName)) + { + log.debug("Deleting person: " + userName); + personService.deletePerson(userName); + } + else + { + log.debug("Not deleting person: " + userName + ", as they don't appear to exist"); + } + } + + // Just adding a little helper method to make the above code more readable. Oh Java 8, how we need you! (Or Groovy, or Scala...) + private T doWorkAs(final String userName, final RetryingTransactionCallback work) + { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + @Override public T doWork() throws Exception { - // userId1 + 5, userId2 + 3 - generateFeed(); - - List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 19, feed.size()); - - // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 8, feed.size()); - - deleteSite(userId1+"pub"); - deleteSite(userId1+"priv1"); - deleteSite(userId1+"priv2"); - deleteSite(userId1+"mod1"); - deleteSite(userId1+"mod2"); - - feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 2, feed.size()); - - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 2, feed.size()); - - deletePerson(userId1); - deletePerson(userId2); - - feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); - assertEquals(feed.toString(), 0, feed.size()); - - feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); - assertEquals(feed.toString(), 0, feed.size()); - - return null; + return transactionHelper.doInTransaction(work); } - }, AuthenticationUtil.getAdminUserName()); + }, userName); + } + + private String prettyJson(List jsonStrings) + { + StringBuilder result = new StringBuilder(); + for (String jsonString : jsonStrings) + { + result.append(prettyJson(jsonString)); + result.append("\n"); + } + return result.toString(); + } + + private String prettyJson(String jsonString) + { + String result = jsonString; + try + { + JSONObject json = new JSONObject(new JSONTokener(jsonString)); + result = json.toString(2); + } catch (JSONException ignored) + { + // Intentionally empty + } + return result; } } \ No newline at end of file diff --git a/source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js b/source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js index 47d52ec163..55abae91fb 100644 --- a/source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js +++ b/source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js @@ -6,9 +6,9 @@ function testPreferences() preferences.comp1.value1 = "value1"; preferences.comp1.value2 = 12; - preferenceService.setPreferences("userOne", preferences); + preferenceService.setPreferences(username, preferences); - var result = preferenceService.getPreferences("userOne"); + var result = preferenceService.getPreferences(username); test.assertNotNull(result); test.assertEquals("myValue", result.myValue); @@ -23,9 +23,9 @@ function testPreferences() preferences.comp1.value1 = "changed"; preferences.comp1.value2 = 1001; - preferenceService.setPreferences("userOne", preferences); + preferenceService.setPreferences(username, preferences); - result = preferenceService.getPreferences("userOne"); + result = preferenceService.getPreferences(username); test.assertNotNull(result); test.assertEquals("myValue", result.myValue); test.assertEquals("changed", result.comp1.value1); @@ -33,18 +33,18 @@ function testPreferences() test.assertEquals("value1", result.comp2.value1); test.assertEquals(3.142, result.comp2.value2); - preferenceService.clearPreferences("userOne", "comp1"); + preferenceService.clearPreferences(username, "comp1"); - result = preferenceService.getPreferences("userOne"); + result = preferenceService.getPreferences(username); test.assertNotNull(result); test.assertEquals("myValue", result.myValue); test.assertEquals("undefined", result.comp1); test.assertEquals("value1", result.comp2.value1); test.assertEquals(3.142, result.comp2.value2); - preferenceService.clearPreferences("userOne"); + preferenceService.clearPreferences(username); - result = preferenceService.getPreferences("userOne"); + result = preferenceService.getPreferences(username); test.assertNotNull(result); test.assertEquals("undefined", result.myValue); test.assertEquals("undefined", result.comp1);