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