diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 7487048516..be6b6d27da 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -506,6 +506,13 @@ + + + + true + + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 3211003767..89cea501d3 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -1152,7 +1152,7 @@ - + @@ -1177,6 +1177,9 @@ + + + diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index e8179c7db1..3bc9e2a841 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -256,7 +256,7 @@ ppm - + ppt pps pot @@ -338,7 +338,7 @@ xbm - + xls diff --git a/config/alfresco/subsystems/googledocs/default/googledocs-context.xml b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml new file mode 100755 index 0000000000..589a57d690 --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml @@ -0,0 +1,57 @@ + + + + + + + alfresco/subsystems/googledocs/default/googledocs-model.xml + + + + + + + ${googledocs.application.name} + + + + + + ${googledocs.spreadsheet.service.name} + + + ${googledocs.application.name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/subsystems/googledocs/default/googledocs-model.xml b/config/alfresco/subsystems/googledocs/default/googledocs-model.xml new file mode 100755 index 0000000000..d8b650348e --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs-model.xml @@ -0,0 +1,71 @@ + + + + + + + + Google Docs Model + unknown + 1.0 + + + + + + + + + + + + + + + + Google Editable + + + Property who's only purpose is to allow the marker aspect to be automatically added. + d:boolean + true + true + + + + + + Google Document + + + GoogleDocs URL + d:text + false + + + GoogleDocs id + d:text + false + + + GoogleDocs Type + d:text + false + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/subsystems/googledocs/default/googledocs.properties b/config/alfresco/subsystems/googledocs/default/googledocs.properties new file mode 100755 index 0000000000..f0289978b1 --- /dev/null +++ b/config/alfresco/subsystems/googledocs/default/googledocs.properties @@ -0,0 +1,16 @@ + +# Enables google editable functionality +googledocs.googleeditable.enabled=false + +# Google docs application name +googledocs.application.name=Alfresco ECM system + +# Google docs URL +googledocs.url=http://docs.google.com/feeds/default/private/full + +# System google docs authentication credentials +#googledocs.username= +#googledocs.password= + +# Google docs spreadsheet service name +googledocs.spreadsheet.service.name=wise \ No newline at end of file diff --git a/config/alfresco/subsystems/googledocs/default/test.docx b/config/alfresco/subsystems/googledocs/default/test.docx new file mode 100755 index 0000000000..e04a931e44 Binary files /dev/null and b/config/alfresco/subsystems/googledocs/default/test.docx differ diff --git a/config/alfresco/subsystems/googledocs/default/test.xlsx b/config/alfresco/subsystems/googledocs/default/test.xlsx new file mode 100644 index 0000000000..64d4a40495 Binary files /dev/null and b/config/alfresco/subsystems/googledocs/default/test.xlsx differ diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index a4d4eb1e3f..d241102671 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -19,11 +19,23 @@ package org.alfresco.repo.coci; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.version.VersionableAspect; import org.alfresco.service.cmr.coci.CheckOutCheckInService; @@ -48,7 +60,7 @@ import org.alfresco.service.namespace.QName; import org.springframework.extensions.surf.util.I18NUtil; /** - * Version operations service implementation + * Check out check in service implementation * * @author Roy Wetherall */ @@ -65,6 +77,14 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService private static final String MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE = "coci_service.err_workingcopy_has_no_mimetype"; private static final String MSG_ALREADY_CHECKEDOUT = "coci_service.err_already_checkedout"; private static final String MSG_ERR_CANNOT_RENAME = "coci_service.err_cannot_rename"; + + /** Class policy delegate's */ + private ClassPolicyDelegate beforeCheckOut; + private ClassPolicyDelegate onCheckOut; + private ClassPolicyDelegate beforeCheckIn; + private ClassPolicyDelegate onCheckIn; + private ClassPolicyDelegate beforeCancelCheckOut; + private ClassPolicyDelegate onCancelCheckOut; /** * Extension character, used to recalculate the working copy names @@ -101,6 +121,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService */ private SearchService searchService; + /** Policy component */ + private PolicyComponent policyComponent; + /** * The authentication service */ @@ -147,8 +170,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService * * @param copyService the copy service */ - public void setCopyService( - CopyService copyService) + public void setCopyService(CopyService copyService) { this.copyService = copyService; } @@ -158,8 +180,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService * * @param authenticationService the authentication service */ - public void setAuthenticationService( - AuthenticationService authenticationService) + public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; } @@ -194,6 +215,174 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService this.versionableAspect = versionableAspect; } + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Initialise method + */ + public void init() + { + // Register the policies + beforeCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCheckOut.class); + onCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCheckOut.class); + beforeCheckIn = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCheckIn.class); + onCheckIn = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCheckIn.class); + beforeCancelCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.BeforeCancelCheckOut.class); + onCancelCheckOut = policyComponent.registerClassPolicy(CheckOutCheckInServicePolicies.OnCancelCheckOut.class); + } + + /** + * Returns all the classes of a node, including its type and aspects. + * + * @param nodeRef node reference + * @return List list of classes + */ + private List getInvokeClasses(NodeRef nodeRef) + { + List result = new ArrayList(10); + result.add(nodeService.getType(nodeRef)); + Set aspects = nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + result.add(aspect); + } + return result; + } + + /** + * Invoke the before check out policy + * + * @param nodeRef + * @param destinationParentNodeRef + * @param destinationAssocTypeQName + * @param destinationAssocQName + */ + private void invokeBeforeCheckOut( + NodeRef nodeRef, + NodeRef destinationParentNodeRef, + QName destinationAssocTypeQName, + QName destinationAssocQName) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = beforeCheckOut.getList(invokeClass); + for (BeforeCheckOut policy : policies) + { + policy.beforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); + } + + } + } + + /** + * Invoke on the on check out policy + * + * @param workingCopy + */ + private void invokeOnCheckOut(NodeRef workingCopy) + { + List classes = getInvokeClasses(workingCopy); + for (QName invokeClass : classes) + { + Collection policies = onCheckOut.getList(invokeClass); + for (OnCheckOut policy : policies) + { + policy.onCheckOut(workingCopy); + } + + } + } + + /** + * Invoke before check in policy + * + * @param workingCopyNodeRef + * @param versionProperties + * @param contentUrl + * @param keepCheckedOut + */ + private void invokeBeforeCheckIn( + NodeRef workingCopyNodeRef, + Map versionProperties, + String contentUrl, + boolean keepCheckedOut) + { + List classes = getInvokeClasses(workingCopyNodeRef); + for (QName invokeClass : classes) + { + Collection policies = beforeCheckIn.getList(invokeClass); + for (BeforeCheckIn policy : policies) + { + policy.beforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); + } + + } + } + + /** + * Invoke on check in policy + * + * @param nodeRef + */ + private void invokeOnCheckIn(NodeRef nodeRef) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = onCheckIn.getList(invokeClass); + for (OnCheckIn policy : policies) + { + policy.onCheckIn(nodeRef); + } + + } + } + + /** + * Invoke before cancel check out + * + * @param workingCopy + */ + private void invokeBeforeCancelCheckOut(NodeRef workingCopy) + { + List classes = getInvokeClasses(workingCopy); + for (QName invokeClass : classes) + { + Collection policies = beforeCancelCheckOut.getList(invokeClass); + for (BeforeCancelCheckOut policy : policies) + { + policy.beforeCancelCheckOut(workingCopy); + } + + } + } + + /** + * Invoke on cancel check out + * + * @param nodeRef + */ + private void invokeOnCancelCheckOut(NodeRef nodeRef) + { + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = onCancelCheckOut.getList(invokeClass); + for (OnCancelCheckOut policy : policies) + { + policy.onCancelCheckOut(nodeRef); + } + + } + } + /** * @see org.alfresco.service.cmr.coci.CheckOutCheckInService#checkout(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) */ @@ -221,6 +410,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService this.nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null); } + // Invoke before check out policy + invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); + // Rename the working copy String copyName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); copyName = createWorkingCopyName(copyName); @@ -251,9 +443,12 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService workingCopyProperties.put(ContentModel.PROP_WORKING_COPY_OWNER, userName); this.nodeService.addAspect(workingCopy, ContentModel.ASPECT_WORKING_COPY, workingCopyProperties); - // Lock the origional node + // Lock the original node this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + // Invoke on check out policy + invokeOnCheckOut(workingCopy); + // Return the working copy return workingCopy; } @@ -309,115 +504,111 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Check that the working node still has the copy aspect applied if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) { - // Disable versionable behaviours since we don't want the auto version policy behaviour to execute when we check-in - //this.versionableAspect.disableAutoVersion(); - //try - //{ - Map workingCopyProperties = nodeService.getProperties(workingCopyNodeRef); - // Try and get the original node reference - nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE); - if(nodeRef == null) + // Invoke policy + invokeBeforeCheckIn(workingCopyNodeRef, versionProperties, contentUrl, keepCheckedOut); + + Map workingCopyProperties = nodeService.getProperties(workingCopyNodeRef); + // Try and get the original node reference + nodeRef = (NodeRef) workingCopyProperties.get(ContentModel.PROP_COPY_REFERENCE); + if(nodeRef == null) + { + // Error since the original node can not be found + throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); + } + + try + { + // Release the lock + this.lockService.unlock(nodeRef); + } + catch (UnableToReleaseLockException exception) + { + throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); + } + + if (contentUrl != null) + { + ContentData contentData = (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT); + if (contentData == null) { - // Error since the original node can not be found - throw new CheckOutCheckInServiceException(MSG_ERR_BAD_COPY); - } - - try - { - // Release the lock - this.lockService.unlock(nodeRef); - } - catch (UnableToReleaseLockException exception) - { - throw new CheckOutCheckInServiceException(MSG_ERR_NOT_OWNER, exception); - } - - if (contentUrl != null) - { - ContentData contentData = (ContentData) workingCopyProperties.get(ContentModel.PROP_CONTENT); - if (contentData == null) - { - throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); - } - else - { - contentData = new ContentData( - contentUrl, - contentData.getMimetype(), - contentData.getSize(), - contentData.getEncoding()); - } - // Set the content url value onto the working copy - this.nodeService.setProperty( - workingCopyNodeRef, - ContentModel.PROP_CONTENT, - contentData); - } - - // Copy the contents of the working copy onto the original - this.copyService.copy(workingCopyNodeRef, nodeRef); - - // Handle name change on working copy (only for folders/files) - if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) - { - String origName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - String name = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); - if (hasWorkingCopyNameChanged(name, origName)) - { - // ensure working copy has working copy label in its name to avoid name clash - if (!name.contains(" " + getWorkingCopyLabel())) - { - try - { - fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); - } - } - try - { - // rename original to changed working name - fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); - } - catch (FileExistsException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); - } - catch (FileNotFoundException e) - { - throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); - } - } - } - - if (versionProperties != null && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) - { - // Create the new version - this.versionService.createVersion(nodeRef, versionProperties); - } - - if (keepCheckedOut == false) - { - // Delete the working copy - this.nodeService.deleteNode(workingCopyNodeRef); + throw new AlfrescoRuntimeException(MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE, new Object[]{workingCopyNodeRef}); } else { - // Re-lock the original node - this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + contentData = new ContentData( + contentUrl, + contentData.getMimetype(), + contentData.getSize(), + contentData.getEncoding()); } - //} - //finally - //{ - // this.versionableAspect.enableAutoVersion(); - //} + // Set the content url value onto the working copy + this.nodeService.setProperty( + workingCopyNodeRef, + ContentModel.PROP_CONTENT, + contentData); + } + // Copy the contents of the working copy onto the original + this.copyService.copy(workingCopyNodeRef, nodeRef); + + // Handle name change on working copy (only for folders/files) + if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) + { + String origName = (String)this.nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + String name = (String)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_NAME); + if (hasWorkingCopyNameChanged(name, origName)) + { + // ensure working copy has working copy label in its name to avoid name clash + if (!name.contains(" " + getWorkingCopyLabel())) + { + try + { + fileFolderService.rename(workingCopyNodeRef, createWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, createWorkingCopyName(name)); + } + } + try + { + // rename original to changed working name + fileFolderService.rename(nodeRef, getNameFromWorkingCopyName(name)); + } + catch (FileExistsException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, origName, getNameFromWorkingCopyName(name)); + } + catch (FileNotFoundException e) + { + throw new CheckOutCheckInServiceException(e, MSG_ERR_CANNOT_RENAME, name, getNameFromWorkingCopyName(name)); + } + } + } + + if (versionProperties != null && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true) + { + // Create the new version + this.versionService.createVersion(nodeRef, versionProperties); + } + + if (keepCheckedOut == false) + { + // Delete the working copy + this.nodeService.deleteNode(workingCopyNodeRef); + } + else + { + // Re-lock the original node + this.lockService.lock(nodeRef, LockType.READ_ONLY_LOCK); + } + + // Invoke policy + invokeOnCheckIn(nodeRef); } else { @@ -466,6 +657,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Ensure that the node has the copy aspect if (this.nodeService.hasAspect(workingCopyNodeRef, ContentModel.ASPECT_COPIEDFROM) == true) { + // Invoke policy + invokeBeforeCancelCheckOut(workingCopyNodeRef); + // Get the original node nodeRef = (NodeRef)this.nodeService.getProperty(workingCopyNodeRef, ContentModel.PROP_COPY_REFERENCE); if (nodeRef == null) @@ -479,6 +673,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService // Delete the working copy this.nodeService.deleteNode(workingCopyNodeRef); + + // Invoke policy + invokeOnCancelCheckOut(nodeRef); } else { diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java new file mode 100755 index 0000000000..0297021500 --- /dev/null +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServicePolicies.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2010 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.coci; + +import java.io.Serializable; +import java.util.Map; + +import org.alfresco.repo.policy.ClassPolicy; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Policy interfaces for the check in/check out service + * + * @author Roy Wetherall + */ +public interface CheckOutCheckInServicePolicies +{ + /** + * + */ + public interface BeforeCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCheckOut"); + + /** + * + * @param nodeRef + */ + void beforeCheckOut( + NodeRef nodeRef, + NodeRef destinationParentNodeRef, + QName destinationAssocTypeQName, + QName destinationAssocQName); + } + + /** + * + */ + public interface OnCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCheckOut"); + + /** + * + * @param nodeRef + */ + void onCheckOut(NodeRef workingCopy); + } + + /** + * + */ + public interface BeforeCheckIn extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCheckIn"); + + /** + * + * @param workingCopyNodeRef + * @param versionProperties + * @param contentUrl + * @param keepCheckedOut + */ + void beforeCheckIn( + NodeRef workingCopyNodeRef, + Map versionProperties, + String contentUrl, + boolean keepCheckedOut); + } + + /** + * + */ + public interface OnCheckIn extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCheckIn"); + + /** + * + * @param nodeRef + */ + void onCheckIn(NodeRef nodeRef); + } + + /** + * + */ + public interface BeforeCancelCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCancelCheckOut"); + + /** + * + * @param nodeRef + */ + void beforeCancelCheckOut(NodeRef workingCopyNodeRef); + } + + /** + * + */ + public interface OnCancelCheckOut extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "onCancelCheckOut"); + + /** + * + * @param nodeRef + */ + void onCancelCheckOut(NodeRef nodeRef); + } +} diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index d87b3fc20c..48fdeaa56d 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -29,7 +29,6 @@ import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; -import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionServiceImpl; import org.alfresco.repo.copy.CopyBehaviourCallback.ChildAssocCopyAction; @@ -38,7 +37,6 @@ import org.alfresco.repo.copy.CopyBehaviourCallback.CopyChildAssociationDetails; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; @@ -54,17 +52,16 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.security.AccessPermission; -import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; -import org.springframework.extensions.surf.util.ParameterCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; +import org.springframework.extensions.surf.util.ParameterCheck; /** * Node operations service implmentation. diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java new file mode 100755 index 0000000000..bd13306260 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsModel.java @@ -0,0 +1,37 @@ +/* +* Copyright (C) 2005-2010 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.googledocs; + +import org.alfresco.service.namespace.QName; + +/** + * Google docs model constants + */ +public interface GoogleDocsModel +{ + static final String GOOGLE_DOCS_PREFIX = "gd"; + static final String GOOGLE_DOCS_MODEL_1_0_URI = "http://www.alfresco.org/model/googledocs/1.0"; + + static final QName ASPECT_GOOGLEEDITABLE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "googleEditable"); + + static final QName ASPECT_GOOGLERESOURCE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "googleResource"); + static final QName PROP_URL = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "url"); + static final QName PROP_RESOURCE_ID = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "resourceId"); + static final QName PROP_RESOURCE_TYPE = QName.createQName(GOOGLE_DOCS_MODEL_1_0_URI, "resourceType"); +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java new file mode 100644 index 0000000000..42c4edc344 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsPermissionContext.java @@ -0,0 +1,15 @@ +/** + * + */ +package org.alfresco.repo.googledocs; + +/** + * Google docs permission context + */ +public enum GoogleDocsPermissionContext +{ + PRIVATE, + SHARE_READ, + SHARE_WRITE, + SHARE_READWRITE +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java new file mode 100755 index 0000000000..4e59c55e11 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsService.java @@ -0,0 +1,43 @@ +package org.alfresco.repo.googledocs; + +import java.io.InputStream; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Google docs integration service interface + */ +public interface GoogleDocsService +{ + /** + * Create a google doc from a given node. The content of the node will be used + * as a basis of the associated google doc. If the node has no content a new, empty google + * doc of the correct type will be created. + * + * The permission context provides information about how google sharing permissions should be + * set on the created google doc. + * + * @param nodeRef node reference + * @param permissionContext permission context + */ + void createGoogleDoc(NodeRef nodeRef, GoogleDocsPermissionContext permissionContext); + + /** + * Deletes the google resource associated with the node reference. This could be a folder or + * document. + * + * @param nodeRef node reference + */ + void deleteGoogleResource(NodeRef nodeRef); + + /** + * Gets the content as an input stream of google doc associated with the given node. The + * node must have the google resource aspect and the associated resource should not be a + * folder. + * + * @param nodeRef node reference + * @return InputStream the content of the associated google doc + */ + InputStream getGoogleDocContent(NodeRef nodeRef); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java new file mode 100755 index 0000000000..aee0e5d746 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java @@ -0,0 +1,814 @@ +/* +* Copyright (C) 2005-2010 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.googledocs; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentService; +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.security.AccessPermission; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.google.gdata.client.GoogleService; +import com.google.gdata.client.docs.DocsService; +import com.google.gdata.data.IEntry; +import com.google.gdata.data.MediaContent; +import com.google.gdata.data.PlainTextConstruct; +import com.google.gdata.data.acl.AclEntry; +import com.google.gdata.data.acl.AclFeed; +import com.google.gdata.data.acl.AclRole; +import com.google.gdata.data.acl.AclScope; +import com.google.gdata.data.docs.DocumentEntry; +import com.google.gdata.data.docs.DocumentListEntry; +import com.google.gdata.data.docs.FolderEntry; +import com.google.gdata.data.docs.PresentationEntry; +import com.google.gdata.data.media.MediaSource; +import com.google.gdata.data.media.MediaStreamSource; +import com.google.gdata.util.AuthenticationException; +import com.google.gdata.util.ContentType; +import com.google.gdata.util.ServiceException; + +/** + * + */ +public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel +{ + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(GoogleDocsServiceImpl.class); + + public static final String TYPE_DOCUMENT = "document"; + public static final String TYPE_SPREADSHEET = "spreadsheet"; + public static final String TYPE_PRESENTATION = "presentation"; + public static final String TYPE_PDF = "pdf"; + + /** Services */ + private DocsService googleDocumentService; + private GoogleService spreadsheetsService; + private NodeService nodeService; + private ContentService contentService; + private PersonService personService; + private MimetypeService mimetypeService; + private PermissionService permissionService; + private OwnableService ownableService; + private AuthorityService authorityService; + + /** GoogleDoc base feed url */ + private String url = "http://docs.google.com/feeds/default/private/full"; + + /** Authentication credentials */ + private boolean initialised = false; + private String username; + private String password; + + private Map permissionMap; + + /** + * @param googleDocumentService google document service + */ + public void setGoogleDocumentService(DocsService googleDocumentService) + { + this.googleDocumentService = googleDocumentService; + } + + /** + * @param spreadsheetsService spread sheets service + */ + public void setSpreadsheetsService(GoogleService spreadsheetsService) + { + this.spreadsheetsService = spreadsheetsService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * @param personService person service + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * @param mimetypeService mime type service + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + /** + * @param permissionService permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + /* + * @param ownableService ownable service + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * @param authorityService authority service + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * @param url root googleDoc URL + */ + public void setUrl(String url) + { + this.url = url; + } + + /** + * @param username google service user name + */ + public void setUsername(String username) + { + this.username = username; + } + + /** + * @param password google service password + */ + public void setPassword(String password) + { + this.password = password; + } + + /** + * @param permissionMap permission map + */ + public void setPermissionMap(Map permissionMap) + { + this.permissionMap = permissionMap; + } + + /** + * Initialise google docs services + */ + public void initialiseGoogleDocsService() + { + if (initialised == false) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Trying to initialise google docs service for user " + username); + } + + if (username == null ||username.length() == 0 || password == null) + { + throw new AlfrescoRuntimeException("No Goolge Docs credentials found. Please set the Google Docs authentication configuration."); + } + + try + { + googleDocumentService.setUserCredentials(username, password); + spreadsheetsService.setUserCredentials(username, password); + googleDocumentService.setChunkedMediaUpload(-1); + } + catch (AuthenticationException e) + { + throw new AlfrescoRuntimeException("Unable to connect to Google Docs. Please check the Google Docs authentication configuration.", e); + } + + initialised = true; + if (logger.isDebugEnabled() == true) + { + logger.debug("Successfully initialised google docs service for user " + username); + } + } + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#upload(org.alfresco.service.cmr.repository.NodeRef) + */ + public void createGoogleDoc(NodeRef nodeRef, GoogleDocsPermissionContext permissionContext) + { + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + // Get property values + String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + + // TODO should be checking to make sure this doesn't already have an associated google doc + + // Get content reader + String mimetype = null; + InputStream is = null; + ContentReader contentReader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + if (contentReader == null) + { + // Determine the mimetype from the file extension + mimetype = mimetypeService.guessMimetype(name); + } + else + { + // Get the mime type and input stream from the content reader + mimetype = contentReader.getMimetype(); + is = contentReader.getContentInputStream(); + } + + // Get the parent folder id + DocumentListEntry parentFolder = getParentFolder(nodeRef); + + // Create the new google document + DocumentListEntry document = createGoogleDocument(name, mimetype, parentFolder, is); + + // Set permissions + setGoogleResourcePermissions(nodeRef, document, permissionContext); + + // Set the google document details + setResourceDetails(nodeRef, document); + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#deleteGoogleResource(org.alfresco.service.cmr.repository.NodeRef) + */ + public void deleteGoogleResource(NodeRef nodeRef) + { + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + try + { + if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) + { + // Get the entry + DocumentListEntry entry = getDocumentListEntry(nodeRef); + if (entry == null) + { + throw new AlfrescoRuntimeException("Unable to find google resource to delete for node " + nodeRef.toString()); + } + + // Delete the entry + entry.delete(); + + // Remove the aspect from the node + nodeService.removeAspect(nodeRef, ASPECT_GOOGLERESOURCE); + } + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to delete google resource for the node "+ nodeRef.toString()); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to delete google resource for the node "+ nodeRef.toString()); + } + } + + /** + * + * @param nodeRef + * @param resourceId + * @param permissionContext + */ + private void setGoogleResourcePermissions(NodeRef nodeRef, DocumentListEntry resource, GoogleDocsPermissionContext permissionContext) + { + // Set the owner of the document + String owner = ownableService.getOwner(nodeRef); + setGoogleResourcePermission(resource, AuthorityType.USER, owner, "owner"); + + if (GoogleDocsPermissionContext.PRIVATE.equals(permissionContext) == false) + { + Set accessPermissions = permissionService.getAllSetPermissions(nodeRef); + for (AccessPermission accessPermission : accessPermissions) + { + String authorityName = accessPermission.getAuthority(); + AuthorityType authorityType = accessPermission.getAuthorityType(); + String permission = accessPermission.getPermission(); + if (permissionMap.containsKey(permission) == true) + { + String aclRole = permissionMap.get(permission); + if (GoogleDocsPermissionContext.SHARE_READ.equals(permissionContext) == true && + ("reader".equals(aclRole) == true || "writer".equals(aclRole) == true)) + { + // Set the permission to read + setGoogleResourcePermission(resource, authorityType, authorityName, "reader"); + } + else if (GoogleDocsPermissionContext.SHARE_WRITE.equals(permissionContext) == true && + "writer".equals(aclRole) == true) + { + // Set the permission to write + setGoogleResourcePermission(resource, authorityType, authorityName, "writer"); + } + else if (GoogleDocsPermissionContext.SHARE_READWRITE.equals(permissionContext) == true && + ("reader".equals(aclRole) == true || "writer".equals(aclRole) == true)) + { + // Set the permission to the current acl + setGoogleResourcePermission(resource, authorityType, authorityName, aclRole); + } + + } + } + } + } + + /** + * + * @param resourceId + * @param authorityType + * @param authorityName + * @param role + */ + private void setGoogleResourcePermission(DocumentListEntry resource, AuthorityType authorityType, String authorityName, String role) + { + if (AuthorityType.USER.equals(authorityType) == true) + { + // Set the user permissions on the resource + String userEMail = getUserEMail(authorityName); + if (userEMail != null && userEMail.length() != 0) + { + setGoogleResourcePermission(resource, userEMail, role); + } + } + else if (AuthorityType.GROUP.equals(authorityType) == true) + { + Set childAuthorities = authorityService.getContainedAuthorities(AuthorityType.USER, authorityName, false); + for (String childAuthority : childAuthorities) + { + setGoogleResourcePermission(resource, AuthorityType.USER, childAuthority, role); + } + } + } + + /** + * + * @param userName + * @return + */ + private String getUserEMail(String userName) + { + String email = null; + NodeRef personNodeRef = personService.getPerson(userName); + if (personNodeRef != null) + { + email = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_EMAIL); + } + return email; + } + + /** + * + * @param nodeRef + * @return + */ + private DocumentListEntry getParentFolder(NodeRef nodeRef) + { + DocumentListEntry folder = null; + + NodeRef parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + if (parentNodeRef != null) + { + if (nodeService.hasAspect(parentNodeRef, ASPECT_GOOGLERESOURCE) == true) + { + String resourceType = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_TYPE); + String resourceId = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_ID); + folder = getDocumentListEntry(resourceType + ":" + resourceId); + } + else + { + DocumentListEntry parentFolder = getParentFolder(parentNodeRef); + String name = (String)nodeService.getProperty(parentNodeRef, ContentModel.PROP_NAME); + folder = createGoogleFolder(name, parentFolder); + + setResourceDetails(parentNodeRef, folder); + } + } + + return folder; + } + + /** + * + * @param nodeRef + * @param folderId + */ + private void setResourceDetails(final NodeRef nodeRef, final DocumentListEntry documentListEntry) + { + AuthenticationUtil.RunAsWork runAsWork = new AuthenticationUtil.RunAsWork() + { + public Object doWork() throws Exception + { + // Create a map of the property values + Map props = new HashMap(1); + props.put(GoogleDocsModel.PROP_RESOURCE_ID, documentListEntry.getDocId()); + props.put(GoogleDocsModel.PROP_RESOURCE_TYPE, documentListEntry.getType()); + props.put(GoogleDocsModel.PROP_URL, documentListEntry.getDocumentLink().getHref()); + + // Add the google resource aspect + nodeService.addAspect(nodeRef, GoogleDocsModel.ASPECT_GOOGLERESOURCE, props); + return null; + } + }; + + // Run as admin + AuthenticationUtil.runAs(runAsWork, AuthenticationUtil.getAdminUserName()); + } + + /** + * @see org.alfresco.google.docs.GoogleDocsService#getGoogleDocContent(org.alfresco.service.cmr.repository.NodeRef) + */ + public InputStream getGoogleDocContent(NodeRef nodeRef) + { + InputStream result = null; + + // Check for mandatory parameters + ParameterCheck.mandatory("nodeRef", nodeRef); + + // Initialise google doc services + initialiseGoogleDocsService(); + + try + { + if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) + { + String downloadUrl = null; + DocumentListEntry document = getDocumentListEntry(nodeRef); + String docType = document.getType(); + + ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + String fileExtension = mimetypeService.getExtension(contentData.getMimetype()); + if (fileExtension.equals("docx")) + { + fileExtension = "doc"; + } + + if (docType.equals(TYPE_DOCUMENT) || docType.equals(TYPE_PRESENTATION)) + { + downloadUrl = ((MediaContent)document.getContent()).getUri() + "&exportFormat=" + fileExtension; + } + else if (docType.equals(TYPE_SPREADSHEET)) + { + downloadUrl = ((MediaContent)document.getContent()).getUri() + "&exportFormat=" + fileExtension; + + // If exporting to .csv or .tsv, add the gid parameter to specify which sheet to export + if (fileExtension.equals("csv") || fileExtension.equals("tsv")) + { + downloadUrl += "&gid=0"; // gid=0 will download only the first sheet + } + } + else if (docType.equals(TYPE_PDF)) + { + MediaContent mc = (MediaContent)document.getContent(); + downloadUrl = mc.getUri(); + } + else + { + throw new AlfrescoRuntimeException("Unsuported document type: " + docType); + } + + // TODO need to verify that download of a spreadsheet works before we delete this historical code ... + +// UserToken docsToken = null; +// if (isSpreadSheet) +// { +// docsToken = (UserToken) googleDocumentService.getAuthTokenFactory().getAuthToken(); +// UserToken spreadsheetsToken = (UserToken) spreadsheetsService.getAuthTokenFactory().getAuthToken(); +// googleDocumentService.setUserToken(spreadsheetsToken.getValue()); +// +// } + + MediaContent mc = new MediaContent(); + mc.setUri(downloadUrl); + MediaSource ms = googleDocumentService.getMedia(mc); + + // if (isSpreadSheet) + // { + // googleDocumentService.setUserToken(docsToken.getValue()); + // } + + result = ms.getInputStream(); + } + else + { + // error since we are trying to download a non-google resource + throw new AlfrescoRuntimeException("Can not download google doc content since no corresponsing google resource could be found"); + } + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to get google document stream.", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to get google document stream.", e); + } + + return result; + } + + /** + * + * @param docNodeRef + * @return + */ + private DocumentListEntry getDocumentListEntry(NodeRef docNodeRef) + { + String docType = (String)nodeService.getProperty(docNodeRef, PROP_RESOURCE_TYPE); + String docId = (String)nodeService.getProperty(docNodeRef, PROP_RESOURCE_ID); + return getDocumentListEntry(docType + ":" + docId); + } + + /** + * + * @param docResourceId + * @return + */ + private DocumentListEntry getDocumentListEntry(String docResourceId) + { + return getEntry(docResourceId, DocumentListEntry.class); + } + + /** + * + * @param + * @param resourceId + * @param entryClass + * @return + */ + private E getEntry(String resourceId, Class entryClass) + { + E result = null; + try + { + URL docEntryURL = new URL(url + "/" + resourceId); + result = googleDocumentService.getEntry(docEntryURL, entryClass); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to get document list entry for resource " + resourceId, e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to get document list entry for resource " + resourceId, e); + } + return result; + } + + /** + * + * @param name + * @param mimetype + * @param parentFolder + * @param is + * @return + */ + private DocumentListEntry createGoogleDocument(String name, String mimetype, DocumentListEntry parentFolder, InputStream is) + { + DocumentListEntry document = null; + + try + { + // Create the media content object + MediaContent mediaContent = new MediaContent(); + mediaContent.setMimeType(new ContentType(mimetype)); + + if (is != null) + { + mediaContent.setMediaSource(new MediaStreamSource(is, mimetype)); + } + + // Parent folder url + String parentFolderUrl = url; + if (parentFolder != null) + { + parentFolderUrl = ((MediaContent)parentFolder.getContent()).getUri(); + } + + // Create the document entry object + DocumentListEntry docEntry = null; + if (MimetypeMap.MIMETYPE_EXCEL.equals(mimetype) == true) + { + docEntry = new PresentationEntry(); + } + else + { + docEntry = new DocumentEntry(); + } + + docEntry.setContent(mediaContent); + docEntry.setTitle(new PlainTextConstruct(name)); + + // Upload the document into the parent folder + document = googleDocumentService.insert( + new URL(parentFolderUrl), + docEntry); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to create google document", e); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to create google document", e); + } + + return document; + } + + /** + * + * @param docResourceId + * @param mimeType + * @param is + */ + private void updateGoogleDocContent(DocumentListEntry document, String mimeType, InputStream is) + { + try + { + // Update the existing content + googleDocumentService.getRequestFactory().setHeader("If-Match", "*"); + document.setMediaSource(new MediaStreamSource(is, mimeType)); + document.updateMedia(false); + googleDocumentService.getRequestFactory().setHeader("If-Match", null); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to update documents content in google docs", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to update documents content in google docs", e); + } + } + + /** + * + * @param folderName + * @param parentFolderId + * @return + */ + private DocumentListEntry createGoogleFolder(String folderName, DocumentListEntry parentFolder) + { + DocumentListEntry folderEntry = null; + + try + { + // Parent folder url + String parentFolderUrl = url; + if (parentFolder != null) + { + parentFolderUrl = ((MediaContent)parentFolder.getContent()).getUri(); + } + + // Create the folder entry + FolderEntry folder = new FolderEntry(); + folder.setTitle(new PlainTextConstruct(folderName)); + + // Create the folder + folderEntry = googleDocumentService.insert( + new URL(parentFolderUrl), + folder); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to create Google Folder", e); + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to create Google Folder", e); + } + + return folderEntry; + } + + /** + * Set permissions on a googleDoc resource + * + * @param resourceId + * @param email + * @param role + */ + private void setGoogleResourcePermission(DocumentListEntry resource, String email, String role) + { + ParameterCheck.mandatory("resource", resource); + ParameterCheck.mandatory("email", email); + ParameterCheck.mandatory("role", role); + + try + { + AclRole aclRole = new AclRole(role); + AclScope scope = new AclScope(AclScope.Type.USER, email); + + // Get the URL + URL aclFeedLinkURL = new URL(resource.getAclFeedLink().getHref()); + + // See if we have already set this permission or not + AclEntry aclEntry = null; + AclFeed aclFeed = googleDocumentService.getFeed(aclFeedLinkURL, AclFeed.class); + if (aclFeed != null) + { + List aclEntries = aclFeed.getEntries(); + for (AclEntry tempAclEntry : aclEntries) + { + AclScope tempScope = tempAclEntry.getScope(); + if (tempScope.equals(scope) == true) + { + // Existing ACL entry found + aclEntry = tempAclEntry; + break; + } + } + } + + if (aclEntry == null) + { + aclEntry = new AclEntry(); + aclEntry.setRole(aclRole); + aclEntry.setScope(scope); + googleDocumentService.insert(aclFeedLinkURL, aclEntry); + } + + // TODO for now we will not 'update' the permissions if they have already been set .... + // + //else + //{ + // AclRole currentAclRole = aclEntry.getRole(); + // if (currentAclRole.toString().equals(aclRole.toString()) == false) + // { + // aclEntry.setRole(aclRole); + // googleDocumentService.update(new URL(aclEntry.getEditLink().getHref()), aclEntry); + // } + //} + } + catch (ServiceException e) + { + throw new AlfrescoRuntimeException("Unable to set premissions on google document", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Unable to set premissions on google document", e); + } + } + + +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java new file mode 100755 index 0000000000..3d6c0aee33 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java @@ -0,0 +1,278 @@ +/* +* Copyright (C) 2005-2010 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.googledocs; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.management.subsystems.ApplicationContextFactory; +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.site.SiteServiceImpl; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ContentReader; +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.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.alfresco.util.PropertyMap; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; + +import com.google.gdata.util.ServiceException; + +public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsModel +{ + private NodeService nodeService; + private GoogleDocsService googleDocsService; + private SiteService siteService; + private TransactionService transactionService; + private FileFolderService fileFolderService; + private ContentService contentService; + private CheckOutCheckInService checkOutCheckInService; + private MutableAuthenticationService authenticationService; + private PersonService personService; + private ApplicationContextFactory subsystem; + + private static final String USER_ONE = "GoogleDocUserOne"; + private static final String USER_TWO = "GoogleDocUserTwo"; + private static final String USER_THREE = "GoogleDocUserThree"; + private static final String USER_FOUR = "GoogleDocUserFour"; + //private static final String EMAIL_DOMAIN = "@alfresco.com"; + + private NodeRef folder = null; + private NodeRef nodeRefDoc = null; + private NodeRef nodeRefSpread = null; + private NodeRef nodeRefPres = null; + private NodeRef nodeRefPdf = null; + private NodeRef nodeRef2 = null; + private UserTransaction userTransaction = null; + + @Override + protected void setUp() throws Exception + { + ApplicationContext appContext = ApplicationContextHelper.getApplicationContext(); + + nodeService = (NodeService)appContext.getBean("nodeService"); + siteService = (SiteService)appContext.getBean("siteService"); + transactionService = (TransactionService)appContext.getBean("transactionService"); + fileFolderService = (FileFolderService)appContext.getBean("fileFolderService"); + contentService = (ContentService)appContext.getBean("contentService"); + checkOutCheckInService = (CheckOutCheckInService)appContext.getBean("checkOutCheckInService"); + authenticationService = (MutableAuthenticationService)appContext.getBean("authenticationService"); + personService = (PersonService)appContext.getBean("personService"); + + // Start the user transaction + userTransaction = transactionService.getUserTransaction(); + userTransaction.begin(); + + // Get the sub-system and make sure the googleeditable feature is turned on + subsystem = (ApplicationContextFactory)appContext.getBean("googledocs"); + if (subsystem.getProperty("googledocs.googleeditable.enabled").equals("false") == true) + { + subsystem.stop(); + subsystem.setProperty("googledocs.googleeditable.enabled", "true"); + subsystem.start(); + } + + // Get the google docs service + ConfigurableApplicationContext childContext = (ConfigurableApplicationContext)subsystem.getApplicationContext(); + googleDocsService = (GoogleDocsService)childContext.getBean("googleDocsService"); + + // Create the test users + createUser(USER_ONE, "rwetherall@alfresco.com"); + createUser(USER_TWO, "rwetherall@activiti.com"); + createUser(USER_THREE, "roy.wetherall@alfresco.com"); + createUser(USER_FOUR, "roy.wetherall@activiti.com"); + + // Authenticate as user one + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE); + + String id = GUID.generate(); + + // Create a site to use as holder for our test google documents + siteService.createSite("sitePreset", id, "My Title", "My Description", SiteVisibility.PUBLIC); + NodeRef container = siteService.createContainer(id, "testComponent", null, null); + + // Add some memberships to the site + siteService.setMembership(id, USER_TWO, SiteServiceImpl.SITE_COLLABORATOR); + siteService.setMembership(id, USER_THREE, SiteServiceImpl.SITE_CONTRIBUTOR); + siteService.setMembership(id, USER_FOUR, SiteServiceImpl.SITE_CONSUMER); + + // Create a folder in our site container + folder = fileFolderService.create(container, "myfolder", ContentModel.TYPE_FOLDER).getNodeRef(); + + // Create test documents + nodeRefDoc = createTestDocument("mydoc.docx", "alfresco/subsystems/googledocs/default/test.docx", MimetypeMap.MIMETYPE_WORD); + nodeRefSpread = createTestDocument("mydoc.xlsx", "alfresco/subsystems/googledocs/default/test.xlsx", MimetypeMap.MIMETYPE_EXCEL); + + // Create an empty content node (simulate creation of a new google doc in UI) + nodeRef2 = fileFolderService.create(folder, "mygoogledoc.doc", ContentModel.TYPE_CONTENT).getNodeRef(); + nodeService.addAspect(nodeRef2, ASPECT_GOOGLEEDITABLE, null); + } + + private NodeRef createTestDocument(String name, String contentPath, String mimetype) + { + NodeRef nodeRef = fileFolderService.create(folder, name, ContentModel.TYPE_CONTENT).getNodeRef(); + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.setEncoding("UTF-8"); + writer.setMimetype(mimetype); + InputStream is = getClass().getClassLoader().getResourceAsStream(contentPath); + writer.putContent(is); + return nodeRef; + } + + private void createUser(String userName, String email) + { + if (authenticationService.authenticationExists(userName) == false) + { + authenticationService.createAuthentication(userName, "PWD".toCharArray()); + + 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); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + personService.createPerson(ppOne); + } + } + + @Override + protected void tearDown() throws Exception + { + if (userTransaction != null) + { + userTransaction.rollback(); + } + } + + public void testGoogleDocUploadDownload() throws Exception + { + googleDocsService.createGoogleDoc(nodeRefDoc, GoogleDocsPermissionContext.SHARE_WRITE); + + assertTrue(nodeService.hasAspect(nodeRefDoc, ASPECT_GOOGLERESOURCE)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_URL)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); + assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); + + System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefDoc, PROP_URL)); + System.out.println("Google doc type: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); + System.out.println("Google doc id: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); + String downloadFile = downloadFile(googleDocsService.getGoogleDocContent(nodeRefDoc), ".doc"); + System.out.println("Download file: " + downloadFile); + +// googleDocsService.upload(nodeRefSpread, GoogleDocsPermissionContext.SHARE_WRITE); +// +// assertTrue(nodeService.hasAspect(nodeRefSpread, ASPECT_GOOGLERESOURCE)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_URL)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_ID)); +// assertNotNull(nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_TYPE)); +// +// System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefSpread, PROP_URL)); +// System.out.println("Google doc type: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_TYPE)); +// System.out.println("Google doc id: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_ID)); +// downloadFile = downloadFile(googleDocsService.download(nodeRefSpread), ".xls"); +// System.out.println("Download file: " + downloadFile); + + } + + public void testCheckOutCheckIn() throws Exception + { + ContentReader contentReader = contentService.getReader(nodeRef2, ContentModel.PROP_CONTENT); + assertNull(contentReader); + + // Check out the empty google document + NodeRef workingCopy = checkOutCheckInService.checkout(nodeRef2); + + assertTrue(nodeService.hasAspect(workingCopy, ASPECT_GOOGLERESOURCE)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_URL)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_RESOURCE_ID)); + assertNotNull(nodeService.getProperty(workingCopy, PROP_RESOURCE_TYPE)); + + System.out.println("Google doc URL: " + nodeService.getProperty(workingCopy, PROP_URL)); + System.out.println("Google doc type: " + nodeService.getProperty(workingCopy, PROP_RESOURCE_TYPE)); + System.out.println("Google doc id: " + nodeService.getProperty(workingCopy, PROP_RESOURCE_ID)); + + checkOutCheckInService.checkin(workingCopy, null); + + assertFalse(nodeService.hasAspect(nodeRefDoc, ASPECT_GOOGLERESOURCE)); + contentReader = contentService.getReader(nodeRef2, ContentModel.PROP_CONTENT); + assertNotNull(contentReader); + } + + /** + * Utility method to download input stream to a file for inspection + * + * @param inStream + * @param ext + * @return + * @throws IOException + * @throws MalformedURLException + * @throws ServiceException + */ + private String downloadFile(InputStream inStream, String ext) throws IOException, MalformedURLException, ServiceException + { + File file = File.createTempFile("googleDocTest", ext); + String filePath = file.getAbsolutePath(); + FileOutputStream outStream = null; + try + { + outStream = new FileOutputStream(filePath); + + int c; + while ((c = inStream.read()) != -1) + { + outStream.write(c); + } + } + finally + { + if (inStream != null) + { + inStream.close(); + } + if (outStream != null) + { + outStream.flush(); + outStream.close(); + } + } + + return filePath; + } +} diff --git a/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java b/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java new file mode 100755 index 0000000000..ecc69f5327 --- /dev/null +++ b/source/java/org/alfresco/repo/googledocs/GoogleEditableAspect.java @@ -0,0 +1,206 @@ +/* +* Copyright (C) 2005-2010 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.googledocs; + +import java.io.InputStream; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.Behaviour.NotificationFrequency; +import org.alfresco.service.cmr.dictionary.DictionaryService; +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.namespace.QName; + +/** + * Behaviour associated with google editable documents + * + */ +public class GoogleEditableAspect implements NodeServicePolicies.OnAddAspectPolicy, + CheckOutCheckInServicePolicies.OnCheckOut, + CheckOutCheckInServicePolicies.OnCheckIn, + CheckOutCheckInServicePolicies.BeforeCancelCheckOut +{ + /** Indicates whether behaviour is enabled or not */ + boolean enabled = false; + + /** Policy component */ + private PolicyComponent policyComponent; + + /** Google docs service */ + private GoogleDocsService googleDocsService; + + /** Node service */ + private NodeService nodeService; + + /** Dictionary service */ + private DictionaryService dictionaryService; + + /** Content service */ + private ContentService contentService; + + /** + * @param enabled true if behaviour enabled, false otherwise + */ + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + /** + * @param policyComponent policy component + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * @param googleDocsService google docs service + */ + public void setGoogleDocsService(GoogleDocsService googleDocsService) + { + this.googleDocsService = googleDocsService; + } + + /** + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param contentService content service + */ + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + /** + * Initialise method + */ + public void init() + { + if (enabled == true) + { + // Register behaviour with policy component + policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE , + new JavaBehaviour(this, "onAddAspect", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(OnCheckOut.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "onCheckOut", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(OnCheckIn.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "onCheckIn", NotificationFrequency.FIRST_EVENT)); + policyComponent.bindClassBehaviour(BeforeCancelCheckOut.QNAME, + GoogleDocsModel.ASPECT_GOOGLEEDITABLE, + new JavaBehaviour(this, "beforeCancelCheckOut", NotificationFrequency.FIRST_EVENT)); + } + } + + /** + * @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) + */ + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + if (nodeService.exists(nodeRef) == true) + { + // Can only make cm:content descendant google editable + QName type = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(type, ContentModel.TYPE_CONTENT) == false) + { + // Prevent aspect from being applied + throw new AlfrescoRuntimeException("The node (" + nodeRef.toString() + ") can not be made google editable, because it is not a sub type of cm:content."); + } + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckOut#onCheckOut(org.alfresco.service.cmr.repository.NodeRef) + */ + public void onCheckOut(NodeRef workingCopy) + { + if (nodeService.exists(workingCopy) == true) + { + // Remove the google editable aspect from the working copy + nodeService.removeAspect(workingCopy, GoogleDocsModel.ASPECT_GOOGLEEDITABLE); + + // Upload the content of the working copy to google docs + googleDocsService.createGoogleDoc(workingCopy, GoogleDocsPermissionContext.SHARE_WRITE); + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.OnCheckIn#onCheckIn(org.alfresco.service.cmr.repository.NodeRef) + */ + public void onCheckIn(NodeRef nodeRef) + { + if (nodeService.exists(nodeRef) == true && + nodeService.hasAspect(nodeRef, GoogleDocsModel.ASPECT_GOOGLERESOURCE) == true) + { + // Get input stream for the google doc + InputStream is = googleDocsService.getGoogleDocContent(nodeRef); + if (is == null) + { + throw new AlfrescoRuntimeException("Unable to complete check in, because the working copy content could not be retrieved from google docs."); + } + + // Write the google content into the node + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + writer.putContent(is); + + // Delete the associated google resource + googleDocsService.deleteGoogleResource(nodeRef); + } + } + + /** + * @see org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCancelCheckOut#beforeCancelCheckOut(org.alfresco.service.cmr.repository.NodeRef) + */ + public void beforeCancelCheckOut(NodeRef workingCopyNodeRef) + { + if (nodeService.exists(workingCopyNodeRef) == true) + { + // Delete the associated google resource + googleDocsService.deleteGoogleResource(workingCopyNodeRef); + } + } +} diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java index 84191579cb..f91c163319 100644 --- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java +++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java @@ -858,6 +858,12 @@ public class SiteServiceImpl implements SiteService, SiteModel } } + /** + * Get the site implementation given a short name + * + * @param shortName + * @return + */ private SiteInfo getSiteImpl(String shortName) { SiteInfo result = null; @@ -873,6 +879,45 @@ public class SiteServiceImpl implements SiteService, SiteModel // Return the site information return result; } + + /** + * @see org.alfresco.service.cmr.site.SiteService#getSite(org.alfresco.service.cmr.repository.NodeRef) + */ + public SiteInfo getSite(NodeRef nodeRef) + { + SiteInfo siteInfo = null; + NodeRef siteNodeRef = getSiteNodeRef(nodeRef); + if (siteNodeRef != null) + { + siteInfo = createSiteInfo(siteNodeRef); + } + return siteInfo; + } + + /** + * Gets the site node reference for a particular node reference + * + * @param nodeRef node reference + * @return NodeRef site node reference or null if node is not in a site + */ + private NodeRef getSiteNodeRef(NodeRef nodeRef) + { + NodeRef siteNodeRef = null; + QName nodeRefType = nodeService.getType(nodeRef); + if (dictionaryService.isSubClass(TYPE_SITE, nodeRefType) == true) + { + siteNodeRef = nodeRef; + } + else + { + ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef); + if (primaryParent != null && primaryParent.getParentRef() != null) + { + siteNodeRef = getSiteNodeRef(primaryParent.getParentRef()); + } + } + return siteNodeRef; + } /** * Gets the site's node reference based on its short name diff --git a/source/java/org/alfresco/service/cmr/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java index f0ff598cc1..6ada2af125 100644 --- a/source/java/org/alfresco/service/cmr/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -98,6 +98,13 @@ public interface SiteService */ SiteInfo getSite(String shortName); + /** + * + * @param nodeRef + * @return + */ + SiteInfo getSite(NodeRef nodeRef); + /** * Update the site information. *