From 44f7dfb7de22e3397e4e28d4ccaaadaa7098e18f Mon Sep 17 00:00:00 2001 From: Jamal Kaabi-Mofrad Date: Tue, 10 May 2016 11:17:59 +0000 Subject: [PATCH] Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 122926 jvonka: (Quick) Shared Links API - updates and fixes - A/C changed from 400 to 409 if shared link already exists - tweak 404 error message (entity id not found) - change response from sharedId to just id - add optional filter when finding links, eg. where=(sharedByUser/id='userId') (userId can also be -me-) - TODO add a few more tests (rendition download, filtered find) RA-708, RA-777 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@126514 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/rest/api/QuickShareLinks.java | 3 +- .../org/alfresco/rest/api/impl/NodesImpl.java | 2 +- .../rest/api/impl/QuickShareLinksImpl.java | 127 ++++++++++-------- .../org/alfresco/rest/api/model/Node.java | 14 +- .../rest/api/model/QuickShareLink.java | 16 ++- .../rest/api/tests/SharedLinkApiTest.java | 20 +-- 6 files changed, 101 insertions(+), 81 deletions(-) diff --git a/source/java/org/alfresco/rest/api/QuickShareLinks.java b/source/java/org/alfresco/rest/api/QuickShareLinks.java index 7e98432396..2517af69e6 100644 --- a/source/java/org/alfresco/rest/api/QuickShareLinks.java +++ b/source/java/org/alfresco/rest/api/QuickShareLinks.java @@ -90,7 +90,8 @@ public interface QuickShareLinks void emailSharedLink(String nodeId, QuickShareLinkEmailRequest emailRequest, Parameters parameters); /** - * Find (search) for shared links visible to current user + * Find (search) for shared links visible to current user. + * Optionally filter by "sharedByUser/id" (if -me- then filter by current user). * * @param parameters * @return diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 567458f9ca..7088d519c6 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -972,7 +972,7 @@ public class NodesImpl implements Nodes List> sortProps = null; if ((sortCols != null) && (sortCols.size() > 0)) { - // TODO should we allow isContent in sort (and map to reverse of isFolder) ? + // TODO should we allow isFile in sort (and map to reverse of isFolder) ? sortProps = new ArrayList<>(sortCols.size()); for (SortColumn sortCol : sortCols) { diff --git a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java index 82c5255cbd..84a42787b4 100644 --- a/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java +++ b/source/java/org/alfresco/rest/api/impl/QuickShareLinksImpl.java @@ -22,16 +22,21 @@ import org.alfresco.model.ContentModel; import org.alfresco.model.QuickShareModel; import org.alfresco.query.PagingRequest; import org.alfresco.repo.quickshare.QuickShareServiceImpl.QuickShareEmailRequest; +import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.rest.antlr.WhereClauseParser; import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.People; import org.alfresco.rest.api.QuickShareLinks; import org.alfresco.rest.api.Renditions; import org.alfresco.rest.api.model.ContentInfo; +import org.alfresco.rest.api.model.Node; import org.alfresco.rest.api.model.QuickShareLink; import org.alfresco.rest.api.model.QuickShareLinkEmailRequest; import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException; import org.alfresco.rest.framework.core.exceptions.DisabledServiceException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; @@ -40,7 +45,12 @@ import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.rest.framework.resource.parameters.where.Query; +import org.alfresco.rest.framework.resource.parameters.where.QueryHelper; +import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; import org.alfresco.service.cmr.quickshare.QuickShareDTO; import org.alfresco.service.cmr.quickshare.QuickShareService; @@ -50,15 +60,18 @@ import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.QueryParameterDefinition; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; +import org.alfresco.util.SearchLanguageConversion; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; @@ -66,9 +79,13 @@ import org.springframework.extensions.surf.util.I18NUtil; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Centralises access to shared link (public "quick share") services and maps between representations. @@ -95,6 +112,8 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean private AuthorityService authorityService; private MimetypeService mimeTypeService; private SearchService searchService; + private DictionaryService dictionaryService; + private NamespaceService namespaceService; public void setServiceRegistry(ServiceRegistry sr) { @@ -134,6 +153,8 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean this.authorityService = sr.getAuthorityService(); this.mimeTypeService = sr.getMimetypeService(); this.searchService = sr.getSearchService(); + this.dictionaryService = sr.getDictionaryService(); + this.namespaceService = sr.getNamespaceService(); } /** @@ -163,7 +184,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean catch (InvalidSharedIdException ex) { logger.warn("Unable to find: " + sharedId); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } } @@ -216,12 +237,12 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean catch (InvalidSharedIdException ex) { logger.warn("Unable to find: " + sharedId); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } catch (InvalidNodeRefException inre) { logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } } @@ -260,12 +281,12 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean catch (InvalidSharedIdException ex) { logger.warn("Unable to find: " + sharedId); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } catch (InvalidNodeRefException inre) { logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } } @@ -303,7 +324,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean String sharedId = (String) nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID); if (sharedId != null) { - throw new InvalidArgumentException("sharedId already exists: "+nodeId+" ["+sharedId+"]"); + throw new ConstraintViolatedException("sharedId already exists: "+nodeId+" ["+sharedId+"]"); } // Note: will throw AccessDeniedException (=> 403) via QuickShareService (when NodeService tries to getAspects) @@ -325,7 +346,7 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean catch (InvalidNodeRefException inre) { logger.warn("Unable to create shared link: [" + nodeRef + "]"); - throw new EntityNotFoundException("Unable to create shared link: " + nodeId); + throw new EntityNotFoundException(nodeId); } } @@ -365,22 +386,46 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean // Helper find (search) method + private static final String PARAM_SHAREDBY = "sharedByUser/id"; + + private final static Set FIND_SHARED_LINKS_QUERY_PROPERTIES = + new HashSet<>(Arrays.asList(new String[] {PARAM_SHAREDBY})); + public CollectionWithPagingInfo findLinks(Parameters parameters) { checkEnabled(); - /* - String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); - - String queryString = - "ASPECT:\"" + QuickShareModel.ASPECT_QSHARE.toString() + - "\" +@\\{http\\://www.alfresco.org/model/content/1.0\\}" + QuickShareModel.PROP_QSHARE_SHAREDBY.getLocalName() + ":\"" + currentUser + "\""; - */ - String queryString = "ASPECT:\"" + QuickShareModel.ASPECT_QSHARE.toString() + "\""; SearchParameters sp = new SearchParameters(); + + // note: REST API query parameter (ie. where clause filter) - not to be confused with search query ;-) + Query q = parameters.getQuery(); + if (q != null) + { + // filtering via "where" clause + MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(FIND_SHARED_LINKS_QUERY_PROPERTIES, null); + QueryHelper.walk(q, propertyWalker); + + String sharedByUserId = propertyWalker.getProperty(PARAM_SHAREDBY, WhereClauseParser.EQUALS, String.class); + + if (sharedByUserId != null) + { + if (People.DEFAULT_USER.equalsIgnoreCase(sharedByUserId)) + { + sharedByUserId = AuthenticationUtil.getFullyAuthenticatedUser(); + } + + QueryParameterDefinition qpd = new QueryParameterDefImpl(QuickShareModel.PROP_QSHARE_SHAREDBY, dictionaryService.getDataType(DataTypeDefinition.TEXT), + true, sharedByUserId); + sp.addQueryParameterDefinition(qpd); + + String qsharedBy = QuickShareModel.PROP_QSHARE_SHAREDBY.toPrefixString(namespaceService); + queryString += " +@"+SearchLanguageConversion.escapeLuceneQuery(qsharedBy)+":\"${"+qsharedBy+"}\""; + } + } + sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setQuery(queryString); sp.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -438,47 +483,15 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean String mimeTypeName = mimeTypeService.getDisplaysByMimetype().get(mimeType); ContentInfo contentInfo = new ContentInfo(mimeType, mimeTypeName, cd.getSize(), cd.getEncoding()); - // - // modifiedByUser - // - UserInfo modifiedByUser = null; - if (noAuth) - { - // note: if not authenticated then we do not currently return userids (to be consistent with v0 internal - limited disclosure) - modifiedByUser = new UserInfo(null, (String) map.get("modifierFirstName"), (String) map.get("modifierLastName")); - } - else - { - String modifiedByUserId = (String)nodeProps.get(ContentModel.PROP_MODIFIER); - modifiedByUser = new UserInfo(modifiedByUserId, (String) map.get("modifierFirstName"), (String) map.get("modifierLastName")); - } + Map mapUserInfo = new HashMap<>(2); - // - // sharedByUser - // TODO review - should we return for authenticated users only ?? (not exposed by V0 but needed for "find") - // - UserInfo sharedByUser = null; - String sharedByUserId = (String)nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDBY); - if (sharedByUserId != null) - { - NodeRef pRef = personService.getPerson(sharedByUserId); - if (pRef != null) - { - PersonService.PersonInfo pInfo = personService.getPerson(pRef); - if (pInfo != null) - { - if (noAuth) - { - // note: if not authenticated then we do not currently return userids (to be consistent with v0 internal - limited disclosure) - sharedByUser = new UserInfo(null, pInfo.getFirstName(), pInfo.getLastName()); - } - else - { - sharedByUser = new UserInfo(sharedByUserId, pInfo.getFirstName(), pInfo.getLastName()); - } - } - } - } + // note: if not authenticated then we do not currently return userids (to be consistent with v0 internal - limited disclosure) + boolean displayNameOnly = noAuth; + + UserInfo modifiedByUser = Node.lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), mapUserInfo, personService, displayNameOnly); + + // TODO review - should we return sharedByUser for authenticated users only ?? (not exposed by V0 but needed for "find") + UserInfo sharedByUser = Node.lookupUserInfo((String)nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDBY), mapUserInfo, personService, displayNameOnly); // TODO other "properties" (if needed) - eg. cm:title, cm:lastThumbnailModificationData, ... thumbnail info ... @@ -494,12 +507,12 @@ public class QuickShareLinksImpl implements QuickShareLinks, InitializingBean catch (InvalidSharedIdException ex) { logger.warn("Unable to find: " + sharedId); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } catch (InvalidNodeRefException inre) { logger.warn("Unable to find: " + sharedId + " [" + inre.getNodeRef() + "]"); - throw new EntityNotFoundException("Unable to find: " + sharedId); + throw new EntityNotFoundException(sharedId); } } diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index c618f538ee..3bc6ada994 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -109,15 +109,20 @@ public class Node implements Comparable this.modifiedByUser = lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), mapUserInfo, personService); } - public static UserInfo lookupUserInfo(String userName, Map mapUserInfo, PersonService personService) { + public static UserInfo lookupUserInfo(String userName, Map mapUserInfo, PersonService personService) + { + return lookupUserInfo(userName, mapUserInfo, personService, false); + } + public static UserInfo lookupUserInfo(String userName, Map mapUserInfo, PersonService personService, boolean displayNameOnly) + { UserInfo userInfo = mapUserInfo.get(userName); if ((userInfo == null) && (userName != null)) { String sysUserName = AuthenticationUtil.getSystemUserName(); if (userName.equals(sysUserName) || (AuthenticationUtil.isMtEnabled() && userName.startsWith(sysUserName + "@"))) { - userInfo = new UserInfo(userName, userName, ""); + userInfo = new UserInfo((displayNameOnly ? null : userName), userName, ""); } else { @@ -137,14 +142,13 @@ public class Node implements Comparable if (pInfo != null) { - userInfo = new UserInfo(userName, pInfo.getFirstName(), pInfo.getLastName()); + userInfo = new UserInfo((displayNameOnly ? null : userName), pInfo.getFirstName(), pInfo.getLastName()); } else { logger.warn("Unknown person: "+userName); - userInfo = new UserInfo(userName, userName, ""); + userInfo = new UserInfo((displayNameOnly ? null : userName), userName, ""); } - } mapUserInfo.put(userName, userInfo); diff --git a/source/java/org/alfresco/rest/api/model/QuickShareLink.java b/source/java/org/alfresco/rest/api/model/QuickShareLink.java index 95d84cf4b2..5e823a3cdb 100644 --- a/source/java/org/alfresco/rest/api/model/QuickShareLink.java +++ b/source/java/org/alfresco/rest/api/model/QuickShareLink.java @@ -23,9 +23,11 @@ import java.util.Date; /** * Representation of quick share link * - * The "sharedId" provides a short link/url that is easy to copy/paste/send (via email or other). - * As of now, these links are public in that they provide unauthenticated access to the - * node's content and limited metadata info, such as file name and last modifer/modification. + * The shared link id provides a short id that can be part of a short app url that is easy to + * copy/paste/send (via email or other). + * + * As of now, these shared links are public in that they provide unauthenticated access to the + * node's content and limited metadata info, such as file name and last modifier/modification. * * In the future, the QuickShareService *could* be enhanced to provide additional features, * such as link expiry &/or "password" protection, etc. @@ -35,7 +37,7 @@ import java.util.Date; */ public class QuickShareLink { - // unique "short" link (ie. shorter than a guid, 22 vs 36 chars) + // unique short id (ie. shorter than a guid, 22 vs 36 chars) private String sharedId; private String nodeId; @@ -58,11 +60,11 @@ public class QuickShareLink this.nodeId = nodeId; } - public String getSharedId() { + public String getId() { return sharedId; } - public void setSharedId(String sharedId) { + public void setId(String sharedId) { this.sharedId = sharedId; } @@ -129,7 +131,7 @@ public class QuickShareLink public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("QuickShareLink [sharedId=").append(getSharedId()); + sb.append("QuickShareLink [id=").append(getId()); sb.append(", nodeId=").append(getNodeId()); sb.append(", name=").append(getName()); sb.append(", modifiedAt=").append(getModifiedAt()); diff --git a/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java b/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java index e84b5e7d28..5770053022 100644 --- a/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/SharedLinkApiTest.java @@ -154,7 +154,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest response = post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 201); QuickShareLink resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); - String sharedId = resp.getSharedId(); + String sharedId = resp.getId(); assertNotNull(sharedId); assertEquals(d1Id, resp.getNodeId()); @@ -175,14 +175,14 @@ public class SharedLinkApiTest extends AbstractBaseApiTest assertEquals(user2+" "+user2, resp.getSharedByUser().getDisplayName()); // -ve test - try to create again (same user) - already exists - post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 400); + post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 409); // auth access to get shared link info response = getSingle(QuickShareLinkEntityResource.class, user1, sharedId, null, 200); resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); - assertEquals(sharedId, resp.getSharedId()); + assertEquals(sharedId, resp.getId()); assertEquals(d1Id, resp.getNodeId()); assertEquals(docName1, resp.getName()); @@ -194,7 +194,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest response = getSingle(QuickShareLinkEntityResource.class, null, sharedId, null, 200); resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); - assertEquals(sharedId, resp.getSharedId()); + assertEquals(sharedId, resp.getId()); assertEquals(d1Id, resp.getNodeId()); assertEquals(docName1, resp.getName()); @@ -230,7 +230,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest // As user 1 ... // -ve test - try to create again (different user, that has read permission) - already exists - post(URL_SHARED_LINKS, user1, toJsonAsStringNonNull(body), 400); + post(URL_SHARED_LINKS, user1, toJsonAsStringNonNull(body), 409); // -ve - create - missing nodeId body = new HashMap<>(); @@ -336,7 +336,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest body.put("nodeId", d1Id); response = post(URL_SHARED_LINKS, user2, toJsonAsStringNonNull(body), 201); QuickShareLink resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); - String shared1Id = resp.getSharedId(); + String shared1Id = resp.getId(); // As user 1 ... @@ -345,7 +345,7 @@ public class SharedLinkApiTest extends AbstractBaseApiTest body.put("nodeId", d2Id); response = post(URL_SHARED_LINKS, user1, toJsonAsStringNonNull(body), 201); resp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), QuickShareLink.class); - String shared2Id = resp.getSharedId(); + String shared2Id = resp.getId(); // // find links @@ -354,15 +354,15 @@ public class SharedLinkApiTest extends AbstractBaseApiTest response = getAll(URL_SHARED_LINKS, user1, paging, 200); sharedLinks = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), QuickShareLink.class); assertEquals(2, sharedLinks.size()); - assertEquals(shared2Id, sharedLinks.get(0).getSharedId()); + assertEquals(shared2Id, sharedLinks.get(0).getId()); assertEquals(d2Id, sharedLinks.get(0).getNodeId()); - assertEquals(shared1Id, sharedLinks.get(1).getSharedId()); + assertEquals(shared1Id, sharedLinks.get(1).getId()); assertEquals(d1Id, sharedLinks.get(1).getNodeId()); response = getAll(URL_SHARED_LINKS, user2, paging, 200); sharedLinks = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), QuickShareLink.class); assertEquals(1, sharedLinks.size()); - assertEquals(shared1Id, sharedLinks.get(0).getSharedId()); + assertEquals(shared1Id, sharedLinks.get(0).getId()); assertEquals(d1Id, sharedLinks.get(0).getNodeId());