Merged HEAD (5.2) to 5.2.N (5.2.1)

126514 jkaabimofrad: 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/BRANCHES/DEV/5.2.N/root@126858 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Ancuta Morarasu
2016-05-11 11:48:19 +00:00
parent c3282977ec
commit d2fa56e4b1
6 changed files with 101 additions and 81 deletions

View File

@@ -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

View File

@@ -979,7 +979,7 @@ public class NodesImpl implements Nodes
List<Pair<QName, Boolean>> 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)
{

View File

@@ -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<String> FIND_SHARED_LINKS_QUERY_PROPERTIES =
new HashSet<>(Arrays.asList(new String[] {PARAM_SHAREDBY}));
public CollectionWithPagingInfo<QuickShareLink> 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<String, UserInfo> 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);
}
}

View File

@@ -116,15 +116,20 @@ public class Node implements Comparable<Node>
this.modifiedByUser = lookupUserInfo((String)nodeProps.get(ContentModel.PROP_MODIFIER), mapUserInfo, personService);
}
public static UserInfo lookupUserInfo(String userName, Map<String, UserInfo> mapUserInfo, PersonService personService) {
public static UserInfo lookupUserInfo(String userName, Map<String, UserInfo> mapUserInfo, PersonService personService)
{
return lookupUserInfo(userName, mapUserInfo, personService, false);
}
public static UserInfo lookupUserInfo(String userName, Map<String, UserInfo> 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
{
@@ -144,14 +149,13 @@ public class Node implements Comparable<Node>
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);

View File

@@ -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());

View File

@@ -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());