From 4f7aee643251b0332042a63a3e0cffd4cdb7072a Mon Sep 17 00:00:00 2001 From: Jan Vonka Date: Tue, 18 Sep 2012 14:58:38 +0000 Subject: [PATCH] Merged BRANCHES/DEV/HEAD_QUICK_SHARE_TMP to HEAD: 41501: Creating Quick Share branch for merging into head 41524: Merged THOR1 & THOR1_SPRINTS to HEAD_QUICK_SHARE_TMP 30997: Firefox scrollbars removed on "invitation" and "signup" pages (now using new helper method Alfresco.util.createYUIOverlay) 36011: Merged BRANCHES/DEV/THOR1_QUICK_SHARE to BRANCHES/DEV/THOR1_SPRINTS: 34685: Creating Quick Share branch 34826: First cut of THOR-1270 "F387: As the link receiver, I can view the Document Preview in the browser without having to login" 34868: More on THOR-1270 "F387: As the link receiver, I can view the Document Preview in the browser without having to login" 34901: QuickShare REST API - WIP ... note: API will change :-) 34933: QuickShare REST API - WIP 34934: QuickShare REST API - WIP 34941: QuickShare REST API - WIP ... note: API will change :-) 34989: QuickShare REST API - WIP 34995: QuickShare REST API - WIP 34996: QuickShare REST API - WIP 35011: QuickShare/PublicView REST API 35025: F387: As the link receiver, I can view the Document Preview in the browser without having to login 35035: QuickShare/PublicView REST API 35052: QuickShare/PublicView REST API 35069: More on THOR-1270 "F387 As the link receiver, I can view the Document Preview in the browser without having to login" 35094: Removed cpnfig property that was commited by mistake 35111: First cut of THOR-1268 "F378: As a user I can choose to share a document" 35252: THOR-1271 "F388: From the view page, I can easily sign up or login for Alfresco. Sign Up is embedded in the page and Login will redirect you to the Alfresco Login page" partial commit 35254: First cut of THOR-1268 "F378: As a user I can choose to share a document" part 2 35255: THOR-1270 "F387: As the link receiver, I can view the Document Preview in the browser without having to login" 35264: QuickShare/PublicView REST API 35317: THOR-1322: New metadata doesn't return "webpreview" in thumbnails (part 1) 35319: QuickShare REST API - fix tenant ref when share'ing 35330: QuickShare REST API - fix THOR-1322: New metadata doesn't return "webpreview" in thumbnails (part 2) 35368: QuickShare REST API - THOR-1336: (F418) Get context (nodeRef, siteId, tenantDomain) for a shared_id 35376: QuickShare REST API - fix THOR-1273 (unshare document) 35424: THOR-1271 "F388: From the view page, I can easily sign up or login for Alfresco. Sign Up is embedded in the page and Login will redirect you to the Alfresco Login page" partial commit 35425: THOR-1271 "F388: From the view page, I can easily sign up or login for Alfresco. Sign Up is embedded in the page and Login will redirect you to the Alfresco Login page" partial commit 35566: Fixed THOR-1268 "F378: As a user I can choose to share a document" 35617: THOR-1350: Update Slingshot API (doclib2) - make the "qshare:sharedBy" return full details (instead of just userName) 35624: THOR-1339: QuickShare REST API 35682: Fixed THOR-1268, THOR-1339 & THOR-1269 35935: QuickShare REST API - add "system.quickshare.enabled" prop (if set to false then disables QuickShare service/API) 35996: Fixed THOR-1369 & THOR-1270 35997: Fixed THOR-1369 & THOR-1270 part 2 36000: Fixed THOR-1369 & THOR-1270 part 3 41550: Merged THOR1 & THOR1_SPRINTS to HEAD_QUICK_SHARE_TMP part 2 41569: Updated web.xml to add support for URLRewrite filtering for Quick Share 41572: Merged THOR1 & THOR1_SPRINTS to HEAD_QUICK_SHARE_TMP part 3 41640: Merged THOR1_SPRINTS to HEAD_QUICK_SHARE_TMP 36082: THOR-1270 "F387: As the link receiver, I can view the Document Preview in the browser without having to login" - Re-added "Document Details" link public share page git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@41736 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../quickshare/content-noauth.get.desc.xml | 19 ++ .../content-thumbnail-noauth.get.desc.xml | 15 + .../quickshare/metadata-noauth.get.desc.xml | 12 + .../quickshare/metadata-noauth.get.json.ftl | 2 + .../quickshare/metadata.get.desc.xml | 12 + .../quickshare/metadata.get.json.ftl | 2 + .../repository/quickshare/metadata.lib.ftl | 43 +++ .../repository/quickshare/share.get.desc.xml | 12 + .../repository/quickshare/share.get.json.ftl | 8 + .../repository/quickshare/share.post.desc.xml | 17 ++ .../repository/quickshare/share.post.json.ftl | 5 + .../quickshare/unshare.delete.desc.xml | 14 + .../quickshare/unshare.delete.json.ftl | 5 + .../web-scripts-application-context.xml | 60 ++++ .../quickshare/AbstractQuickShareContent.java | 98 +++++++ .../web/scripts/quickshare/MetaDataGet.java | 190 +++++++++++++ .../quickshare/QuickShareContentGet.java | 174 ++++++++++++ .../quickshare/QuickShareMetaDataGet.java | 100 +++++++ .../quickshare/QuickShareRestApiTest.java | 260 ++++++++++++++++++ .../QuickShareThumbnailContentGet.java | 190 +++++++++++++ .../scripts/quickshare/ShareContentGet.java | 108 ++++++++ .../scripts/quickshare/ShareContentPost.java | 135 +++++++++ .../quickshare/UnshareContentDelete.java | 173 ++++++++++++ 23 files changed, 1654 insertions(+) create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-noauth.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-thumbnail-noauth.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.lib.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.json.ftl create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/AbstractQuickShareContent.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/MetaDataGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareMetaDataGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentPost.java create mode 100644 source/java/org/alfresco/repo/web/scripts/quickshare/UnshareContentDelete.java diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-noauth.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-noauth.get.desc.xml new file mode 100644 index 0000000000..8d419184a1 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-noauth.get.desc.xml @@ -0,0 +1,19 @@ + + QuickShare - get some "shared" content + + if QuickShare exists then gets the content stream for the specified document + + TODO: + - do we need both options (/node/content/{shared_id} vs /node/{shared_id}/content) ? + ]]> + + + /api/internal/shared/node/content{property}/{shared_id}?a={attach?} + /api/internal/shared/node/{shared_id}/content{property}?a={attach?} + + argument + none + + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-thumbnail-noauth.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-thumbnail-noauth.get.desc.xml new file mode 100644 index 0000000000..e6766cf166 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/content-thumbnail-noauth.get.desc.xml @@ -0,0 +1,15 @@ + + QuickShare - get some "shared" thumbnail content + + if QuickShare exists then gets the content stream for the specified document thumbnail/rendition + ]]> + + + /api/internal/shared/node/{shared_id}/content{property}/thumbnails/{thumbnailname}?a={attach?} + + argument + none + + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.desc.xml new file mode 100644 index 0000000000..e8f45ae162 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.desc.xml @@ -0,0 +1,12 @@ + +QuickShare - get some limited metadata for "shared" content (including thumbnail defs) + + if QuickShare exists then gets the limited metadata for the specified document + ]]> + + /api/internal/shared/node/{shared_id}/metadata + + none + required + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.json.ftl new file mode 100644 index 0000000000..99c80e2628 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata-noauth.get.json.ftl @@ -0,0 +1,2 @@ +<#import "metadata.lib.ftl" as metadataLib /> +<@metadataLib.resultsJSON item=item /> \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.desc.xml new file mode 100644 index 0000000000..fbce00923a --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.desc.xml @@ -0,0 +1,12 @@ + +Get some limited metadata for content (including thumbnail defs) + + + + /api/node/{store_protocol}/{store_id}/{node_id}/metadata + + user + required + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.json.ftl new file mode 100644 index 0000000000..99c80e2628 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.get.json.ftl @@ -0,0 +1,2 @@ +<#import "metadata.lib.ftl" as metadataLib /> +<@metadataLib.resultsJSON item=item /> \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.lib.ftl new file mode 100644 index 0000000000..1fe70144ce --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/metadata.lib.ftl @@ -0,0 +1,43 @@ +<#macro resultsJSON item> +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "name": "${item.name}", + "title": "${item.title!""}", + "mimetype": "${item.mimetype!""}", + "size": ${item.size?c}, + "modifiedOn": "${xmldate(item.modified)}", + "modifier": + { + "firstName": "${item.modifierFirstName!""}", + "lastName": "${item.modifierLastName!""}" + }, + "thumbnailDefinitions": + [ + <#if item.thumbnailDefinitions??> + <#list item.thumbnailDefinitions as thumbnailDefinition> + "${thumbnailDefinition}" + <#if thumbnailDefinition_has_next>, + + + ], + "thumbnails": + [ + <#if item.thumbnailNames??> + <#list item.thumbnailNames as thumbnailName> + "${thumbnailName}" + <#if thumbnailName_has_next>, + + + ], + "lastThumbnailModificationData": + [ + <#if item.lastThumbnailModificationData??> + <#list item.lastThumbnailModificationData as lastThumbnailMod> + "${lastThumbnailMod}" + <#if lastThumbnailMod_has_next>, + + + ] +} + + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.desc.xml new file mode 100644 index 0000000000..ed5efe3fea --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.desc.xml @@ -0,0 +1,12 @@ + +Get some node context (nodeRef, tenantDomain, siteId) for given shared_id + + + + /api/internal/shared/share/{shared_id} + + user + required + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.json.ftl new file mode 100644 index 0000000000..618bf81e7b --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.get.json.ftl @@ -0,0 +1,8 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "sharedId" : "${sharedId}", + "nodeRef" : "${nodeRef}", + "tenantDomain" : "${tenantDomain}", + "siteId" : "${siteId}" +} + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.desc.xml new file mode 100644 index 0000000000..23821924d0 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.desc.xml @@ -0,0 +1,17 @@ + + QuickShare - "share" some content => enable public/unauthenticated access + + + + + /api/internal/shared/share/{store_protocol}/{store_id}/{node_id} + + argument + user + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.json.ftl new file mode 100644 index 0000000000..b083e2fbe4 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/share.post.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "sharedId" : "${sharedId}" +} + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.desc.xml new file mode 100644 index 0000000000..2453ebe38d --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.desc.xml @@ -0,0 +1,14 @@ + + QuickShare - "unshare" some content => disable public/unauthenticated access + + + + + /api/internal/shared/unshare/{shared_id} + + argument + user + internal + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.json.ftl new file mode 100644 index 0000000000..99e4ef07c6 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/quickshare/unshare.delete.json.ftl @@ -0,0 +1,5 @@ +<#escape x as jsonUtils.encodeJSONString(x)> +{ + "success" : ${success?string} +} + \ No newline at end of file diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 3ca598456c..6979d55294 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1840,4 +1840,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/AbstractQuickShareContent.java b/source/java/org/alfresco/repo/web/scripts/quickshare/AbstractQuickShareContent.java new file mode 100644 index 0000000000..fc8cb41980 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/AbstractQuickShareContent.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.util.Pair; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.WebScriptException; + + +/** + * QuickShare/PublicView + * + * @author janv + */ +public abstract class AbstractQuickShareContent extends DeclarativeWebScript +{ + protected NodeService nodeService; + protected AttributeService attributeService; + protected TenantService tenantService; + + private boolean enabled = true; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setAttributeService(AttributeService attributeService) + { + this.attributeService = attributeService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + protected boolean isEnabled() + { + return this.enabled; + } + + + protected Pair getTenantNodeRefFromSharedId(final String sharedId) + { + return getTenantNodeRefFromSharedId(attributeService, tenantService, sharedId); + } + + /* package */ static Pair getTenantNodeRefFromSharedId(final AttributeService attributeService, final TenantService tenantService, final String sharedId) + { + final NodeRef nodeRef = TenantUtil.runAsDefaultTenant(new TenantRunAsWork() + { + public NodeRef doWork() throws Exception + { + return (NodeRef)attributeService.getAttribute(ShareContentPost.ATTR_KEY_SHAREDIDS_ROOT, sharedId); + } + }); + + if (nodeRef == null) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: " + sharedId); + } + + // note: relies on tenant-specific (ie. mangled) nodeRef + String tenantDomain = tenantService.getDomain(nodeRef.getStoreRef().getIdentifier()); + + return new Pair(tenantDomain, tenantService.getBaseName(nodeRef)); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/MetaDataGet.java b/source/java/org/alfresco/repo/web/scripts/quickshare/MetaDataGet.java new file mode 100644 index 0000000000..47d4d9f514 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/MetaDataGet.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.thumbnail.ThumbnailDefinition; +import org.alfresco.repo.web.scripts.WebScriptUtil; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.NoSuchPersonException; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.thumbnail.ThumbnailService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * QuickShare/PublicView + * + * GET web script to get limited metadata (including thumbnail defs) => authenticated web script (using a nodeRef) + * + * Note: authenticated web script (equivalent to unauthenticated version - see QuickShareMetaDataGet) + * + * @author janv + */ +public class MetaDataGet extends AbstractQuickShareContent +{ + private static final Log logger = LogFactory.getLog(QuickShareMetaDataGet.class); + + private PersonService personService; + private ThumbnailService thumbnailService; + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setThumbnailService(ThumbnailService thumbnailService) + { + this.thumbnailService = thumbnailService; + } + + + @Override + protected Map executeImpl(final WebScriptRequest req, Status status, Cache cache) + { + // create map of params (template vars) + Map params = req.getServiceMatch().getTemplateVars(); + final NodeRef nodeRef = WebScriptUtil.getNodeRef(params); + if (nodeRef == null) + { + String msg = "A valid NodeRef must be specified!"; + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, msg); + } + + try + { + Map model = getMetaDataModel(nodeRef); + + if (logger.isDebugEnabled()) + { + logger.debug("Retrieved limited metadata: "+nodeRef+" ["+model+"]"); + } + + return model; + } + catch (InvalidNodeRefException inre) + { + logger.error("Unable to find node: "+inre.getNodeRef()); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find nodeRef: "+inre.getNodeRef()); + } + } + + @SuppressWarnings("unchecked") + protected Map getMetaDataModel(NodeRef nodeRef) + { + QName typeQName = nodeService.getType(nodeRef); + if (! typeQName.equals(ContentModel.TYPE_CONTENT)) + { + throw new InvalidNodeRefException(nodeRef); + } + + Map nodeProps = nodeService.getProperties(nodeRef); + ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + + String modifierUserName = (String)nodeProps.get(ContentModel.PROP_MODIFIER); + Map personProps = null; + if (modifierUserName != null) + { + try + { + NodeRef personRef = personService.getPerson(modifierUserName); + if (personRef != null) + { + personProps = nodeService.getProperties(personRef); + } + } + catch (NoSuchPersonException nspe) + { + // absorb this exception - eg. System (or maybe the user has been deleted) + if (logger.isInfoEnabled()) + { + logger.info("MetaDataGet - no such person: "+modifierUserName); + } + } + } + + Map metadata = new HashMap(8); + + metadata.put("name", nodeProps.get(ContentModel.PROP_NAME)); + metadata.put("title", nodeProps.get(ContentModel.PROP_TITLE)); + + if (contentData != null) + { + metadata.put("mimetype", contentData.getMimetype()); + metadata.put("size", contentData.getSize()); + } + else + { + metadata.put("size", 0L); + } + + metadata.put("modified", nodeProps.get(ContentModel.PROP_MODIFIED)); + + if (personProps != null) + { + metadata.put("modifierFirstName", personProps.get(ContentModel.PROP_FIRSTNAME)); + metadata.put("modifierLastName", personProps.get(ContentModel.PROP_LASTNAME)); + } + + // thumbnail defs for this nodeRef + List thumbnailDefs = new ArrayList(7); + if (contentData != null) + { + // Note: thumbnail defs only appear in this list if they can produce a thumbnail for the content + // found in the content property of this node. This will be determined by looking at the mimetype of the content + // and the destination mimetype of the thumbnail. + List thumbnailDefinitions = thumbnailService.getThumbnailRegistry().getThumbnailDefinitions(contentData.getMimetype(), contentData.getSize()); + for (ThumbnailDefinition thumbnailDefinition : thumbnailDefinitions) + { + thumbnailDefs.add(thumbnailDefinition.getName()); + } + } + metadata.put("thumbnailDefinitions", thumbnailDefs); + + // thumbnail instances for this nodeRef + List thumbnailRefs = thumbnailService.getThumbnails(nodeRef, ContentModel.PROP_CONTENT, null, null); + List thumbnailNames = new ArrayList(thumbnailRefs.size()); + for (NodeRef thumbnailRef : thumbnailRefs) + { + thumbnailNames.add((String)nodeService.getProperty(thumbnailRef, ContentModel.PROP_NAME)); + } + metadata.put("thumbnailNames", thumbnailNames); + + metadata.put("lastThumbnailModificationData", (List)nodeProps.get(ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA)); + + Map model = new HashMap(1); + model.put("item", metadata); + return model; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java new file mode 100644 index 0000000000..4dbed92472 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareContentGet.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.io.IOException; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.QuickShareModel; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.web.scripts.content.ContentGet; +import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + + +/** + * QuickShare/PublicView + * + * GET web script to get stream "shared" content (ie. enabled for public/unauthenticated access) from the repository + * + * WARNING: **unauthenticated** web script (equivalent to authenticated version - see ContentGet.java) + * + * @author janv + */ +public class QuickShareContentGet extends ContentGet +{ + private static final Log logger = LogFactory.getLog(QuickShareContentGet.class); + + private NodeService nodeService; + private NamespaceService namespaceService; + private AttributeService attributeService; + private TenantService tenantService; + + private boolean enabled = true; + + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + super.setNodeService(nodeService); + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + super.setNamespaceService(namespaceService); + } + + public void setAttributeService(AttributeService attributeService) + { + this.attributeService = attributeService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + protected boolean isEnabled() + { + return this.enabled; + } + + + @Override + public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException + { + if (! isEnabled()) + { + throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "QuickShare is disabled system-wide"); + } + + // create map of template vars (params) + final Map params = req.getServiceMatch().getTemplateVars(); + final String sharedId = params.get("shared_id"); + if (sharedId == null) + { + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "A valid sharedId must be specified !"); + } + + try + { + Pair pair = ShareContentPost.getTenantNodeRefFromSharedId(attributeService, tenantService, sharedId); + final String tenantDomain = pair.getFirst(); + final NodeRef nodeRef = pair.getSecond(); + + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Void doWork() throws Exception + { + if (! nodeService.getAspects(nodeRef).contains(QuickShareModel.ASPECT_QSHARE)) + { + throw new InvalidNodeRefException(nodeRef); + } + + executeImpl(nodeRef, params, req, res, null); + + return null; + } + }, tenantDomain); + + if (logger.isDebugEnabled()) + { + logger.debug("QuickShare - retrieved content: "+sharedId+" ["+nodeRef+"]"); + } + + } + catch (InvalidNodeRefException inre) + { + logger.error("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: "+sharedId); + } + } + + protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model) throws IOException + { + // render content + QName propertyQName = ContentModel.PROP_CONTENT; + String contentPart = templateVars.get("property"); + if (contentPart.length() > 0 && contentPart.charAt(0) == ';') + { + if (contentPart.length() < 2) + { + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Content property malformed"); + } + String propertyName = contentPart.substring(1); + if (propertyName.length() > 0) + { + propertyQName = QName.createQName(propertyName, namespaceService); + } + } + + // determine attachment + boolean attach = Boolean.valueOf(req.getParameter("a")); + + // Stream the content + streamContent(req, res, nodeRef, propertyQName, attach, model); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareMetaDataGet.java b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareMetaDataGet.java new file mode 100644 index 0000000000..8b0b8a1c2e --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareMetaDataGet.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.QuickShareModel; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * QuickShare/PublicView + * + * GET web script to get limited metadata (including thumbnail defs) for some "shared" content + * + * WARNING: **unauthenticated** web script (equivalent to authenticated version - see MetaDataGet.java) + * + * @author janv + */ +public class QuickShareMetaDataGet extends MetaDataGet +{ + private static final Log logger = LogFactory.getLog(QuickShareMetaDataGet.class); + + @Override + protected Map executeImpl(final WebScriptRequest req, Status status, Cache cache) + { + if (! isEnabled()) + { + throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "QuickShare is disabled system-wide"); + } + + // create map of params (template vars) + Map params = req.getServiceMatch().getTemplateVars(); + final String sharedId = params.get("shared_id"); + if (sharedId == null) + { + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "A valid sharedId must be specified !"); + } + + try + { + Pair pair = getTenantNodeRefFromSharedId(attributeService, tenantService, sharedId); + final String tenantDomain = pair.getFirst(); + final NodeRef nodeRef = pair.getSecond(); + + Map model = TenantUtil.runAsSystemTenant(new TenantRunAsWork>() + { + public Map doWork() throws Exception + { + if (! nodeService.getAspects(nodeRef).contains(QuickShareModel.ASPECT_QSHARE)) + { + throw new InvalidNodeRefException(nodeRef); + } + + return getMetaDataModel(nodeRef); + } + }, tenantDomain); + + if (logger.isDebugEnabled()) + { + logger.debug("QuickShare - retrieved metadata: "+sharedId+" ["+nodeRef+"]["+model+"]"); + } + + return model; + } + catch (InvalidNodeRefException inre) + { + logger.error("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: "+sharedId); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java new file mode 100644 index 0000000000..197fb5d937 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareRestApiTest.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyMap; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * This class tests QuickShare REST API + * + * @author janv + * @since Cloud/4.1 + */ +public class QuickShareRestApiTest extends BaseWebScriptTest +{ + private static final String USER_ONE = "UserOne"; + private static final String USER_TWO = "UserTwo"; + + private final static String SHARE_URL = "/api/internal/shared/share/{node_ref_3}"; + + private final static String UNSHARE_URL = "/api/internal/shared/unshare/{shared_id}"; + private final static String SHARE_METADATA_URL = "/api/internal/shared/node/{shared_id}/metadata"; + private final static String SHARE_CONTENT_URL = "/api/internal/shared/node/{shared_id}/content"; + private final static String SHARE_CONTENT_THUMBNAIL_URL = "/api/internal/shared/node/{shared_id}/content/thumbnails/{thumbnailname}?c=force"; + + // note: node_ref_3 => three segments, eg. store_protocol/store_id/node_uuid + private final static String AUTH_METADATA_URL = "/api/node/{node_ref_3}/metadata"; + private final static String AUTH_CONTENT_URL = "/api/node/{node_ref_3}/content"; + private final static String AUTH_CONTENT_THUMBNAIL_URL = "/api/node/{node_ref_3}/content/thumbnails/{thumbnailname}?c=force"; + + private static final String APPLICATION_JSON = "application/json"; + + private NodeRef testNode; + private final static String TEST_NAME = "test node"; + private final static String TEST_CONTENT = "test content"; + private final static String TEST_MIMETYPE_TEXT_PLAIN = MimetypeMap.MIMETYPE_TEXT_PLAIN; + private final static String TEST_MIMETYPE_IMAGE_PNG = MimetypeMap.MIMETYPE_IMAGE_PNG; + + private MutableAuthenticationService authenticationService; + private NodeService nodeService; + private PersonService personService; + private PermissionService permissionService; + private ContentService contentService; + private Repository repositoryHelper; + private RetryingTransactionHelper transactionHelper; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + authenticationService = (MutableAuthenticationService) getServer().getApplicationContext().getBean("AuthenticationService"); + nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); + contentService = (ContentService) getServer().getApplicationContext().getBean("ContentService"); + personService = (PersonService) getServer().getApplicationContext().getBean("PersonService"); + permissionService = (PermissionService) getServer().getApplicationContext().getBean("PermissionService"); + repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); + transactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper"); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + createUser(USER_ONE); + createUser(USER_TWO); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + testNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, TEST_NAME); + ChildAssociationRef result = nodeService.createNode(repositoryHelper.getUserHome(personService.getPerson(USER_ONE)), + ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, + ContentModel.TYPE_CONTENT, props); + + NodeRef nodeRef = result.getChildRef(); + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(TEST_MIMETYPE_TEXT_PLAIN); + writer.putContent(TEST_CONTENT); + + return nodeRef; + } + }); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); + + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(testNode, PermissionService.READ)); + + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + if (testNode != null && nodeService.exists(testNode)) + { + nodeService.deleteNode(testNode); + } + return null; + } + }); + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + deleteUser(USER_ONE); + deleteUser(USER_TWO); + } + + public void testSanityCheckUrls() throws Exception + { + final int expectedStatusOK = 200; + final int expectedStatusNotFound = 404; + final int expectedStatusServerError = 500; // currently mapped from AccessDenied (should it be 403, 404 or does it depend on use-case) + + String testNodeRef_3 = testNode.toString().replace("://", "/"); + + // As user one ... + + // get metadata for node (authenticated) + Response rsp = sendRequest(new GetRequest(AUTH_METADATA_URL.replace("{node_ref_3}", testNodeRef_3)), expectedStatusOK, USER_ONE); + JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + String name = jsonRsp.getString("name"); + assertEquals(TEST_NAME, name); + String mimetype = jsonRsp.getString("mimetype"); + assertEquals(TEST_MIMETYPE_TEXT_PLAIN, mimetype); + + // get content for node (authenticated) + rsp = sendRequest(new GetRequest(AUTH_CONTENT_URL.replace("{node_ref_3}", testNodeRef_3)), expectedStatusOK, USER_ONE); + String content = rsp.getContentAsString(); + assertEquals(TEST_CONTENT, content); + + // get content thumbnail for node (authenticated) + rsp = sendRequest(new GetRequest(AUTH_CONTENT_THUMBNAIL_URL.replace("{node_ref_3}", testNodeRef_3).replace("{thumbnailname}", "doclib")), expectedStatusOK, USER_ONE); + String type = rsp.getContentType(); + assertEquals(TEST_MIMETYPE_IMAGE_PNG, type); + + // As user two ... + + rsp = sendRequest(new GetRequest(AUTH_METADATA_URL.replace("{node_ref_3}", testNodeRef_3)), expectedStatusServerError, USER_TWO); + rsp = sendRequest(new GetRequest(AUTH_CONTENT_URL.replace("{node_ref_3}", testNodeRef_3)), expectedStatusServerError, USER_TWO); + rsp = sendRequest(new GetRequest(AUTH_CONTENT_THUMBNAIL_URL.replace("{node_ref_3}", testNodeRef_3).replace("{thumbnailname}", "doclib")), expectedStatusServerError, USER_TWO); + + // As user one ... + + // share + rsp = sendRequest(new PostRequest(SHARE_URL.replace("{node_ref_3}", testNodeRef_3), "", APPLICATION_JSON), expectedStatusOK, USER_ONE); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + String sharedId = jsonRsp.getString("sharedId"); + assertNotNull(sharedId); + assertEquals(22, sharedId.length()); // note: we may have to adjust/remove this check if we change length of id (or it becomes variable length) + + // As user two ... + + // get metadata for share (note: can be unauthenticated) + rsp = sendRequest(new GetRequest(SHARE_METADATA_URL.replace("{shared_id}", sharedId)), expectedStatusOK, USER_TWO); + jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); + name = jsonRsp.getString("name"); + assertEquals(TEST_NAME, name); + mimetype = jsonRsp.getString("mimetype"); + assertEquals(TEST_MIMETYPE_TEXT_PLAIN, mimetype); + + // get content for share (note: can be unauthenticated) + rsp = sendRequest(new GetRequest(SHARE_CONTENT_URL.replace("{shared_id}", sharedId)), expectedStatusOK, USER_TWO); + content = rsp.getContentAsString(); + assertEquals(TEST_CONTENT, content); + + // get content thumbnail for share (note: can be unauthenticated) + rsp = sendRequest(new GetRequest(SHARE_CONTENT_THUMBNAIL_URL.replace("{shared_id}", sharedId).replace("{thumbnailname}", "doclib")), expectedStatusOK, USER_TWO); + type = rsp.getContentType(); + assertEquals(TEST_MIMETYPE_IMAGE_PNG, type); + + // As user one ... + + // unshare + rsp = sendRequest(new DeleteRequest(UNSHARE_URL.replace("{shared_id}", sharedId)), expectedStatusOK, USER_ONE); + + // As user two ... + + // -ve test (should not be able to get metadata or content via sharedId) - whether authenticated or not + rsp = sendRequest(new GetRequest(SHARE_METADATA_URL.replace("{shared_id}", sharedId)), expectedStatusNotFound, USER_TWO); + rsp = sendRequest(new GetRequest(SHARE_CONTENT_URL.replace("{shared_id}", sharedId)), expectedStatusNotFound, USER_TWO); + rsp = sendRequest(new GetRequest(SHARE_CONTENT_THUMBNAIL_URL.replace("{shared_id}", sharedId).replace("{thumbnailname}", "doclib")), expectedStatusNotFound, USER_TWO); + } + + private void createUser(String userName) + { + if (! authenticationService.authenticationExists(userName)) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + } + + if (! personService.personExists(userName)) + { + PropertyMap ppOne = new PropertyMap(4); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + private void deleteUser(String userName) + { + if (personService.personExists(userName)) + { + personService.deletePerson(userName); + } + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java new file mode 100644 index 0000000000..d2d8e430df --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/QuickShareThumbnailContentGet.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.jscript.BaseScopableProcessorExtension; +import org.alfresco.repo.jscript.ScriptNode; +import org.alfresco.repo.thumbnail.script.ScriptThumbnailService; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.thumbnail.ThumbnailService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.mozilla.javascript.Scriptable; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.WebScriptResponse; + + +/** + * QuickShare/PublicView + * + * GET web script to stream "shared" thumbnail content (ie. enabled for public/unauthenticated access) from the repository + * + * WARNING: **unauthenticated** web script (equivalent to authenticated version - see "thumbnail.get.js") + * + * @author janv + */ +public class QuickShareThumbnailContentGet extends QuickShareContentGet +{ + private static final Log logger = LogFactory.getLog(QuickShareContentGet.class); + + private ThumbnailService thumbnailService; + private ScriptThumbnailService scriptThumbnailService; + private ServiceRegistry serviceRegistry; + + public void setThumbnailService(ThumbnailService thumbnailService) + { + this.thumbnailService = thumbnailService; + } + + public void setScriptThumbnailService(ScriptThumbnailService scriptThumbnailService) + { + this.scriptThumbnailService = scriptThumbnailService; + } + + public void setServiceRegistry(ServiceRegistry services) + { + this.serviceRegistry = services; + } + + @Override + protected void executeImpl(NodeRef nodeRef, Map templateVars, WebScriptRequest req, WebScriptResponse res, Map model) throws IOException + { + String thumbnailName = templateVars.get("thumbnailname"); + if (thumbnailName == null) + { + logger.error("Thumbnail name was not provided: "+nodeRef); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find " + nodeRef); + } + + // Indicate whether or not the thumbnail can be cached by the browser. Caching is allowed if the lastModified + // argument is provided as this is an indication of request uniqueness and therefore the browser will have + // the latest thumbnail image. + if (model == null) + { + model = new HashMap(1); + } + + if (req.getParameter("lastModified") != null) + { + model.put("allowBrowserToCache", "true"); // note: must be String not boolean + } + else + { + model.put("allowBrowserToCache", "false"); // note: must be String not boolean + } + + + NodeRef thumbnailNodeRef = thumbnailService.getThumbnailByName(nodeRef, ContentModel.PROP_CONTENT, thumbnailName); + + if (thumbnailNodeRef == null) + { + // Get the queue/force create setting + boolean qc = false; + boolean fc = false; + String c = req.getParameter("c"); + if (c != null) + { + if (c.equals("queue")) + { + qc = true; + } + else if (c.equals("force")) + { + fc = true; + } + } + + // Get the place holder flag + boolean ph = false; + String phString = req.getParameter("ph"); + if (phString != null) + { + ph = new Boolean(phString); + } + + Scriptable scope = new BaseScopableProcessorExtension().getScope(); // note: required for ValueConverter (collection) + ScriptNode node = new ScriptNode(nodeRef, serviceRegistry, scope); + + // Queue the creation of the thumbnail if appropriate + if (fc) + { + ScriptNode thumbnailNode = node.createThumbnail(thumbnailName, false); + if (thumbnailNode != null) + { + thumbnailNodeRef = thumbnailNode.getNodeRef(); + } + } + else + { + if (qc) + { + node.createThumbnail(thumbnailName, true); + } + } + + if (thumbnailNodeRef == null) + { + if (ph == true) + { + // Try and get the place holder resource. We use a method in the thumbnail service + // that by default gives us a resource based on the content's mime type. + String phPath = null; + ContentData contentData = (ContentData)this.serviceRegistry.getNodeService().getProperty(nodeRef, ContentModel.PROP_CONTENT); + if (contentData != null) + { + phPath = scriptThumbnailService.getMimeAwarePlaceHolderResourcePath(thumbnailName, contentData.getMimetype()); + } + + if (phPath == null) + { + // 404 since no thumbnail was found + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Thumbnail was not found and no place holder resource set for '" + thumbnailName + "'"); + } + else + { + // Set the resouce path in the model ready for the content stream to send back to the client + model.put("contentPath", phPath); + } + } + else + { + // 404 since no thumbnail was found + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Thumbnail was not found"); + } + } + } + + super.executeImpl(thumbnailNodeRef, templateVars, req, res, model); + + if (logger.isDebugEnabled()) + { + logger.debug("QuickShare - retrieved thumbnail content: "+thumbnailNodeRef+" ["+nodeRef+","+thumbnailName+"]"); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentGet.java b/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentGet.java new file mode 100644 index 0000000000..aa63dec286 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentGet.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * QuickShare/PublicView + * + * GET web script to lookup some context (nodeRef, tenantDomain, siteId) for a given "Share" + * + * Note: authenticated web script + * + * @author janv + */ +public class ShareContentGet extends AbstractQuickShareContent +{ + private static final Log logger = LogFactory.getLog(ShareContentPost.class); + + protected SiteService siteService; + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + if (! isEnabled()) + { + throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "QuickShare is disabled system-wide"); + } + + // create map of params (template vars) + Map params = req.getServiceMatch().getTemplateVars(); + final String sharedId = params.get("shared_id"); + if (sharedId == null) + { + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "A valid sharedId must be specified !"); + } + + try + { + Pair pair = getTenantNodeRefFromSharedId(attributeService, tenantService, sharedId); + final String tenantDomain = pair.getFirst(); + final NodeRef nodeRef = pair.getSecond(); + + SiteInfo siteInfo = siteService.getSite(nodeRef); + + String siteId = null; + if (siteInfo != null) + { + siteId = siteInfo.getShortName(); + } + + Map model = new HashMap(3); + model.put("sharedId", sharedId); + model.put("nodeRef", nodeRef.toString()); + model.put("siteId", siteId); + model.put("tenantDomain", tenantDomain); + + if (logger.isInfoEnabled()) + { + logger.info("QuickShare - get shared context: "+sharedId+" ["+model+"]"); + } + + return model; + } + catch (InvalidNodeRefException inre) + { + logger.error("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: "+sharedId); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentPost.java b/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentPost.java new file mode 100644 index 0000000000..70f3a24d5b --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/ShareContentPost.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.QuickShareModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.web.scripts.WebScriptUtil; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.safehaus.uuid.UUID; +import org.safehaus.uuid.UUIDGenerator; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * QuickShare/PublicView + * + * POST web script to "Share" access to some content (ie. enable unauthenticated access to this node) + * + * Note: authenticated web script + * + * @author janv + */ +public class ShareContentPost extends AbstractQuickShareContent +{ + private static final Log logger = LogFactory.getLog(ShareContentPost.class); + + /* package */ static final String ATTR_KEY_SHAREDIDS_ROOT = ".sharedIds"; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + if (! isEnabled()) + { + throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "QuickShare is disabled system-wide"); + } + + // create map of params (template vars) + Map params = req.getServiceMatch().getTemplateVars(); + final NodeRef nodeRef = WebScriptUtil.getNodeRef(params); + if (nodeRef == null) + { + String msg = "A valid NodeRef must be specified!"; + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, msg); + } + + try + { + QName typeQName = nodeService.getType(nodeRef); + if (! typeQName.equals(ContentModel.TYPE_CONTENT)) + { + throw new InvalidNodeRefException(nodeRef); + } + + final String sharedId; + + if (! nodeService.getAspects(nodeRef).contains(QuickShareModel.ASPECT_QSHARE)) + { + UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID(); + sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) + + Map props = new HashMap(2); + props.put(QuickShareModel.PROP_QSHARE_SHAREDID, sharedId); + props.put(QuickShareModel.PROP_QSHARE_SHAREDBY, AuthenticationUtil.getRunAsUser()); + + nodeService.addAspect(nodeRef, QuickShareModel.ASPECT_QSHARE, props); + + final NodeRef tenantNodeRef = tenantService.getName(nodeRef); + + TenantUtil.runAsDefaultTenant(new TenantRunAsWork() + { + public Void doWork() throws Exception + { + attributeService.setAttribute(tenantNodeRef, ATTR_KEY_SHAREDIDS_ROOT, sharedId); + return null; + } + }); + + if (logger.isInfoEnabled()) + { + logger.info("QuickShare - shared content: "+sharedId+" ["+nodeRef+"]"); + } + } + else + { + sharedId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID); + + if (logger.isDebugEnabled()) + { + logger.debug("QuickShare - content already shared: "+sharedId+" ["+nodeRef+"]"); + } + } + + Map model = new HashMap(1); + model.put("sharedId", sharedId); + return model; + } + catch (InvalidNodeRefException inre) + { + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find node: " + nodeRef); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/web/scripts/quickshare/UnshareContentDelete.java b/source/java/org/alfresco/repo/web/scripts/quickshare/UnshareContentDelete.java new file mode 100644 index 0000000000..be0b5fbbd4 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/quickshare/UnshareContentDelete.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.quickshare; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.model.ContentModel; +import org.alfresco.model.QuickShareModel; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + + +/** + * QuickShare/PublicView + * + * DELETE web script to "Unshare" access to some content (ie. disable unauthenticated access to this node) + * + * Note: authenticated web script + * + * @author janv + */ +public class UnshareContentDelete extends AbstractQuickShareContent implements NodeServicePolicies.BeforeDeleteNodePolicy +{ + private static final Log logger = LogFactory.getLog(ShareContentPost.class); + + protected PolicyComponent policyComponent; + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * The initialise method + */ + public void init() + { + // Register interest in the beforeDeleteNode policy - note: currently for content only !! + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + ContentModel.TYPE_CONTENT, + new JavaBehaviour(this, "beforeDeleteNode")); + } + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + if (! isEnabled()) + { + throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "QuickShare is disabled system-wide"); + } + + // create map of params (template vars) + Map params = req.getServiceMatch().getTemplateVars(); + final String sharedId = params.get("shared_id"); + if (sharedId == null) + { + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "A valid sharedId must be specified !"); + } + + try + { + Pair pair = getTenantNodeRefFromSharedId(attributeService, tenantService, sharedId); + final String tenantDomain = pair.getFirst(); + final NodeRef nodeRef = pair.getSecond(); + + TenantUtil.runAsSystemTenant(new TenantRunAsWork() + { + public Void doWork() throws Exception + { + QName typeQName = nodeService.getType(nodeRef); + if (! typeQName.equals(ContentModel.TYPE_CONTENT)) + { + throw new InvalidNodeRefException(nodeRef); + } + + String nodeSharedId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID); + + if (! EqualsHelper.nullSafeEquals(nodeSharedId, sharedId)) + { + logger.warn("SharedId mismatch: expected="+sharedId+",actual="+nodeSharedId); + } + + nodeService.removeAspect(nodeRef, QuickShareModel.ASPECT_QSHARE); + + return null; + } + }, tenantDomain); + + removeSharedId(sharedId); + + if (logger.isInfoEnabled()) + { + logger.info("QuickShare - unshared content: "+sharedId+" ["+nodeRef+"]"); + } + + Map model = new HashMap(1); + model.put("success", Boolean.TRUE); + return model; + } + catch (InvalidNodeRefException inre) + { + logger.error("Unable to find: "+sharedId+" ["+inre.getNodeRef()+"]"); + throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find: "+sharedId); + } + } + + // behaviour - currently registered for content only !! + // note: will remove "share" even if node is only being archived (ie. moved to trash) => a subsequent restore will *not* restore the "share" + public void beforeDeleteNode(final NodeRef nodeRef) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Void doWork() throws Exception + { + String sharedId = (String)nodeService.getProperty(nodeRef, QuickShareModel.PROP_QSHARE_SHAREDID); + if (sharedId != null) + { + removeSharedId(sharedId); + } + return null; + } + }); + } + + private void removeSharedId(final String sharedId) + { + TenantUtil.runAsDefaultTenant(new TenantRunAsWork() + { + public Void doWork() throws Exception + { + attributeService.removeAttribute(ShareContentPost.ATTR_KEY_SHAREDIDS_ROOT, sharedId); + return null; + } + }); + } +} \ No newline at end of file