diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index a5ce32ae10..de53df7b8d 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -471,6 +471,7 @@ + diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index f4d146f038..8bf26ec08a 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -47,7 +47,9 @@ import org.alfresco.model.ContentModel; import org.alfresco.model.QuickShareModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.Client; import org.alfresco.repo.action.executer.ContentMetadataExtracter; +import org.alfresco.repo.activities.ActivityType; import org.alfresco.repo.content.ContentLimitViolationException; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.model.Repository; @@ -99,6 +101,7 @@ import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.activities.ActivityPoster; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -159,6 +162,7 @@ import org.springframework.http.MediaType; public class NodesImpl implements Nodes { private static final Log logger = LogFactory.getLog(NodesImpl.class); + private static final String APP_TOOL = "API"; private enum Type { @@ -181,6 +185,7 @@ public class NodesImpl implements Nodes private OwnableService ownableService; private AuthorityService authorityService; private ThumbnailService thumbnailService; + private ActivityPoster poster; private BehaviourFilter behaviourFilter; @@ -254,6 +259,11 @@ public class NodesImpl implements Nodes this.defaultIgnoreTypesAndAspects = ignoreTypesAndAspects; } + public void setPoster(ActivityPoster poster) + { + this.poster = poster; + } + // excluded namespaces (aspects and properties) private static final List EXCLUDED_NS = Arrays.asList(NamespaceService.SYSTEM_MODEL_1_0_URI); @@ -1482,6 +1492,7 @@ public class NodesImpl implements Nodes private NodeRef createNodeImpl(NodeRef parentNodeRef, String nodeName, QName nodeTypeQName, Map props) { + NodeRef newNode = null; if (props == null) { props = new HashMap<>(1); @@ -1493,13 +1504,24 @@ public class NodesImpl implements Nodes QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(nodeName)); try { - return nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, assocQName, nodeTypeQName, props).getChildRef(); + newNode = nodeService.createNode(parentNodeRef, ContentModel.ASSOC_CONTAINS, assocQName, nodeTypeQName, props).getChildRef(); } catch (DuplicateChildNodeNameException dcne) { // duplicate - name clash throw new ConstraintViolatedException(dcne.getMessage()); } + + boolean isFolder = isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER); + boolean isContent = isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT); + + if (isFolder || isContent) + { + FileInfo fileInfo = fileFolderService.getFileInfo(newNode); + poster.postSiteAwareFileFolderActivity(isFolder?ActivityType.FOLDER_ADDED:ActivityType.FILE_ADDED, null, TenantUtil.getCurrentDomain(), + null, parentNodeRef, newNode, nodeName, APP_TOOL, Client.asType(Client.ClientType.script), fileInfo); + } + return newNode; } // check cm:cmobject (but *not* cm:systemfolder) @@ -1715,7 +1737,16 @@ public class NodesImpl implements Nodes } } - return getFolderOrDocument(nodeRef.getId(), parameters); + Node updatedNode = getFolderOrDocument(nodeRef.getId(), parameters); + boolean isContent = isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT); + + if (isContent) + { + FileInfo fileInfo = fileFolderService.getFileInfo(nodeRef); + poster.postSiteAwareFileFolderActivity(ActivityType.FILE_UPDATED, null, TenantUtil.getCurrentDomain(), + null, updatedNode.getParentId(), updatedNode.getNodeRef(), updatedNode.getName(), APP_TOOL, Client.asType(Client.ClientType.script), fileInfo); + } + return updatedNode; } @Override @@ -1855,12 +1886,14 @@ public class NodesImpl implements Nodes } String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT); - return updateExistingFile(nodeRef, contentInfo, stream, parameters, versionMajor, versionComment); + final String fileName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + return updateExistingFile(null, nodeRef, fileName, contentInfo, stream, parameters, versionMajor, versionComment); } - private Node updateExistingFile(NodeRef nodeRef, BasicContentInfo contentInfo, InputStream stream, Parameters parameters, Boolean versionMajor, String versionComment) + private Node updateExistingFile(NodeRef parentNodeRef, NodeRef nodeRef, String fileName, BasicContentInfo contentInfo, InputStream stream, Parameters parameters, Boolean versionMajor, String versionComment) { boolean isVersioned = versionService.isVersioned(nodeRef); + FileInfo fileInfo = fileFolderService.getFileInfo(nodeRef); behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); try @@ -1877,6 +1910,9 @@ public class NodesImpl implements Nodes createVersion(nodeRef, isVersioned, versionType, versionComment); } + poster.postSiteAwareFileFolderActivity(ActivityType.FILE_UPDATED, null, TenantUtil.getCurrentDomain(), + null, parentNodeRef, nodeRef, fileName, APP_TOOL, Client.asType(Client.ClientType.script), fileInfo); + extractMetadata(nodeRef); } finally @@ -2081,7 +2117,7 @@ public class NodesImpl implements Nodes { // overwrite existing (versionable) file BasicContentInfo contentInfo = new ContentInfoImpl(content.getMimetype(), content.getEncoding(), -1, null); - return updateExistingFile(existingFile, contentInfo, content.getInputStream(), parameters, majorVersion, versionComment); + return updateExistingFile(parentNodeRef, existingFile, fileName, contentInfo, content.getInputStream(), parameters, majorVersion, versionComment); } else { diff --git a/source/test-java/org/alfresco/rest/api/tests/ActivitiesPostingTest.java b/source/test-java/org/alfresco/rest/api/tests/ActivitiesPostingTest.java new file mode 100644 index 0000000000..16ffc15c1b --- /dev/null +++ b/source/test-java/org/alfresco/rest/api/tests/ActivitiesPostingTest.java @@ -0,0 +1,143 @@ +package org.alfresco.rest.api.tests; + +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; +import static org.junit.Assert.*; + +import org.alfresco.repo.activities.ActivityType; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.rest.api.Activities; +import org.alfresco.rest.api.tests.client.HttpResponse; +import org.alfresco.rest.api.tests.client.PublicApiClient; +import org.alfresco.rest.api.tests.client.data.Activity; +import org.alfresco.rest.api.tests.client.data.ContentInfo; +import org.alfresco.rest.api.tests.client.data.Document; +import org.alfresco.rest.api.tests.client.data.Folder; +import org.alfresco.rest.api.tests.util.RestApiUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.json.simple.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by gethin on 22/03/16. + */ +public class ActivitiesPostingTest extends AbstractBaseApiTest +{ + protected MutableAuthenticationService authenticationService; + protected PersonService personService; + + RepoService.TestNetwork networkOne; + RepoService.TestPerson u1; + RepoService.TestSite tSite; + NodeRef docLibNodeRef; + + @Override + public String getScope() + { + return "public"; + } + + @Before + public void setup() throws Exception + { + authenticationService = applicationContext.getBean("authenticationService", MutableAuthenticationService.class); + personService = applicationContext.getBean("personService", PersonService.class); + + networkOne = getTestFixture().getRandomNetwork(); + u1 = networkOne.createUser(); + tSite = createSite(networkOne, u1, SiteVisibility.PRIVATE); + + AuthenticationUtil.setFullyAuthenticatedUser(u1.getId()); + docLibNodeRef = tSite.getContainerNodeRef("documentLibrary"); + AuthenticationUtil.clearCurrentSecurityContext(); + } + + + @After + public void tearDown() throws Exception + { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (personService.personExists(u1.getId())) + { + authenticationService.deleteAuthentication(u1.getId()); + personService.deletePerson(u1.getId()); + } + return null; + } + }); + AuthenticationUtil.clearCurrentSecurityContext(); + } + + @Test + public void testCreateUpdate() throws Exception + { + String folder1 = "folder" + System.currentTimeMillis() + "_1"; + Folder createdFolder = createFolder(u1.getId(), docLibNodeRef.getId(), folder1, null); + assertNotNull(createdFolder); + + Document d1 = new Document(); + d1.setName("d1.txt"); + d1.setNodeType("cm:content"); + ContentInfo ci = new ContentInfo(); + ci.setMimeType("text/plain"); + d1.setContent(ci); + + // create empty file + HttpResponse response = post(getNodeChildrenUrl(createdFolder.getId()), u1.getId(), toJsonAsStringNonNull(d1), 201); + Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); + + //Update the file + Document dUpdate = new Document(); + dUpdate.setName("d1b.txt"); + response = put(URL_NODES, u1.getId(), documentResp.getId(), toJsonAsStringNonNull(dUpdate), null, 200); + + repoService.generateFeed(); + + Map meParams = new HashMap<>(); + meParams.put("who", String.valueOf(Activities.ActivityWho.me)); + PublicApiClient.ListResponse activities = publicApiClient.people().getActivities(u1.getId(), meParams); + assertEquals(activities.getList().size(),3); + Activity act = matchActivity(activities.getList(), ActivityType.FOLDER_ADDED, u1.getId(), tSite.getSiteId(), docLibNodeRef.getId(), folder1); + assertNotNull(act); + + act = matchActivity(activities.getList(), ActivityType.FILE_ADDED, u1.getId(), tSite.getSiteId(), createdFolder.getId(), d1.getName()); + assertNotNull(act); + + act = matchActivity(activities.getList(), ActivityType.FILE_UPDATED, u1.getId(), tSite.getSiteId(), createdFolder.getId(), dUpdate.getName()); + assertNotNull(act); + + } + + private Activity matchActivity(List list, String type, String user, String siteId, String parentId, String title) + { + for (Activity act:list) + { + if (type.equals(act.getActivityType()) + && user.equals(act.getPostPersonId()) + && siteId.equals(act.getSiteId()) + && parentId.equals(act.getSummary().get("parentObjectId")) + && title.equals((act.getSummary().get("title")))) + { + return act; + } + } + return null; + } +}