diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml
index df3ecd83be..de3cf06e96 100644
--- a/config/alfresco/core-services-context.xml
+++ b/config/alfresco/core-services-context.xml
@@ -652,6 +652,7 @@
webdav:lockScope
+
@@ -788,7 +789,6 @@
-
diff --git a/config/alfresco/deployment-service-context.xml b/config/alfresco/deployment-service-context.xml
index 561ce9db8e..b3b2783f22 100644
--- a/config/alfresco/deployment-service-context.xml
+++ b/config/alfresco/deployment-service-context.xml
@@ -58,10 +58,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/messages/templates-messages.properties b/config/alfresco/messages/templates-messages.properties
index 66d9c2ad30..91a58e7e2e 100644
--- a/config/alfresco/messages/templates-messages.properties
+++ b/config/alfresco/messages/templates-messages.properties
@@ -12,6 +12,7 @@ templates.show_audit.application=Application
templates.show_audit.service=Service
templates.show_audit.method=Method
templates.show_audit.timestamp=Timestamp
+templates.show_audit.values=Audit Entry Values
templates.show_audit.failed=Failed
templates.show_audit.message=Message
templates.show_audit.arg_1=Arg 1
diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml
index cfd254c95a..83df8e0955 100644
--- a/config/alfresco/node-services-context.xml
+++ b/config/alfresco/node-services-context.xml
@@ -209,6 +209,15 @@
+
+
+
+ ${spaces.archive.store}
+ ${version.store.version2Store}
+
+
+
+
@@ -222,14 +231,11 @@
-
- ${spaces.archive.store}
- ${version.store.version2Store}
-
+
-
+
diff --git a/config/alfresco/ownable-services-context.xml b/config/alfresco/ownable-services-context.xml
index 11e3c2ca4f..57e1ffe30c 100644
--- a/config/alfresco/ownable-services-context.xml
+++ b/config/alfresco/ownable-services-context.xml
@@ -15,5 +15,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties
index 76e335528d..9757a72da1 100644
--- a/config/alfresco/repository.properties
+++ b/config/alfresco/repository.properties
@@ -436,6 +436,7 @@ spaces.templates.email.workflowemailnotification.childname=cm:workflownotificati
spaces.nodetemplates.childname=app:node_templates
# ADM VersionStore Configuration
+version.store.enableAutoVersioning=true
version.store.deprecated.lightWeightVersionStore=workspace://lightWeightVersionStore
version.store.version2Store=workspace://version2Store
diff --git a/config/alfresco/subsystems/thirdparty/default/swf-transform.properties b/config/alfresco/subsystems/thirdparty/default/swf-transform.properties
index ec7d2c21ff..83fec62c75 100644
--- a/config/alfresco/subsystems/thirdparty/default/swf-transform.properties
+++ b/config/alfresco/subsystems/thirdparty/default/swf-transform.properties
@@ -3,5 +3,6 @@ swf.exe=./bin/pdf2swf
# This option on pdf2swf improves the transformation of graphics-heavy pdfs. See ALF-3580.
# poly2bitmap improves the chances of successful transformation. On its own it reduces
-# the resolution of embedded images. subpixels sets the dpi for embedded images.
-swf.encoder.params=-s poly2bitmap,subpixels=72
+# the resolution of embedded images. subpixels sets the dpi for embedded images.
+# zoom introduced and -s added before each option (ALF-9417).
+swf.encoder.params=-s zoom=72 -s ppmsubpixels=1 -s poly2bitmap=1 -s bitmapfonts=1
diff --git a/config/alfresco/templates/content/examples/show_audit.ftl b/config/alfresco/templates/content/examples/show_audit.ftl
index a72b90a80d..3c353f04e5 100644
--- a/config/alfresco/templates/content/examples/show_audit.ftl
+++ b/config/alfresco/templates/content/examples/show_audit.ftl
@@ -6,81 +6,27 @@
${message("templates.show_audit.user_name")} |
${message("templates.show_audit.application")} |
- ${message("templates.show_audit.service")} |
${message("templates.show_audit.method")} |
${message("templates.show_audit.timestamp")} |
- ${message("templates.show_audit.failed")} |
- ${message("templates.show_audit.message")} |
- ${message("templates.show_audit.arg_1")} |
- ${message("templates.show_audit.arg_2")} |
- ${message("templates.show_audit.arg_3")} |
- ${message("templates.show_audit.arg_4")} |
- ${message("templates.show_audit.arg_5")} |
- ${message("templates.show_audit.return")} |
- ${message("templates.show_audit.thowable")} |
- ${message("templates.show_audit.tx")} |
+ ${message("templates.show_audit.values")} |
<#list document.auditTrail as t>
${t.userIdentifier} |
${t.auditApplication} |
- <#if t.auditService?exists>
- ${t.auditService} |
- <#else>
- |
- #if>
<#if t.auditMethod?exists>
${t.auditMethod} |
<#else>
|
#if>
- ${t.date} |
- <#if t.fail?exists>
- ${t.fail?string("FAILED", "OK")} |
+ ${t.date?datetime} |
+ <#if t.values?exists>
+
+ <@hashMap map=t.values />
+ |
<#else>
|
#if>
- <#if t.message?exists>
- ${t.message} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[0]?exists>
- ${t.methodArgumentsAsStrings[0]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[1]?exists>
- ${t.methodArgumentsAsStrings[1]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[2]?exists>
- ${t.methodArgumentsAsStrings[2]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[3]?exists>
- ${t.methodArgumentsAsStrings[3]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[4]?exists>
- ${t.methodArgumentsAsStrings[4]} |
- <#else>
- |
- #if>
- <#if t.returnObjectAsString?exists>
- ${t.returnObjectAsString} |
- <#else>
- |
- #if>
- <#if t.throwableAsString?exists>
- ${t.throwableAsString} |
- <#else>
- |
- #if>
- ${t.txId} |
#list>
@@ -91,83 +37,74 @@
${message("templates.show_audit.user_name")} |
${message("templates.show_audit.application")} |
- ${message("templates.show_audit.service")} |
${message("templates.show_audit.method")} |
${message("templates.show_audit.timestamp")} |
- ${message("templates.show_audit.failed")} |
- ${message("templates.show_audit.message")} |
- ${message("templates.show_audit.arg_1")} |
- ${message("templates.show_audit.arg_2")} |
- ${message("templates.show_audit.arg_3")} |
- ${message("templates.show_audit.arg_4")} |
- ${message("templates.show_audit.arg_5")} |
- ${message("templates.show_audit.return")} |
- ${message("templates.show_audit.thowable")} |
- ${message("templates.show_audit.tx")} |
+ ${message("templates.show_audit.values")} |
<#list space.auditTrail as t>
${t.userIdentifier} |
${t.auditApplication} |
- <#if t.auditService?exists>
- ${t.auditService} |
- <#else>
- |
- #if>
<#if t.auditMethod?exists>
${t.auditMethod} |
<#else>
|
#if>
- ${t.date} |
- <#if t.fail?exists>
- ${t.fail?string("FAILED", "OK")} |
+ ${t.date?datetime} |
+ <#if t.values?exists>
+
+ <@hashMap map=t.values />
+ |
<#else>
|
#if>
- <#if t.message?exists>
- ${t.message} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[0]?exists>
- ${t.methodArgumentsAsStrings[0]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[1]?exists>
- ${t.methodArgumentsAsStrings[1]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[2]?exists>
- ${t.methodArgumentsAsStrings[2]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[3]?exists>
- ${t.methodArgumentsAsStrings[3]} |
- <#else>
- |
- #if>
- <#if t.methodArgumentsAsStrings[4]?exists>
- ${t.methodArgumentsAsStrings[4]} |
- <#else>
- |
- #if>
- <#if t.returnObjectAsString?exists>
- ${t.returnObjectAsString} |
- <#else>
- |
- #if>
- <#if t.throwableAsString?exists>
- ${t.throwableAsString} |
- <#else>
- |
- #if>
- ${t.txId} |
#list>
- #if>
\ No newline at end of file
+ #if>
+
+<#-- renders an audit entry values -->
+<#macro hashMap map simpleMode=false>
+
+ <#assign index = 0 />
+ <#list map?keys as key>
+ <#if simpleMode>
+ - <@parseValue value=key />=<@parseValue value=map?values[index] />
+ <#else>
+ <#assign value = map[key] />
+ <#if value?is_sequence>
+ - <@parseValue value=key />=
+
+ <#list value as element>
+ - <@parseValue value=element />
+ #list>
+
+
+ <#elseif value?is_hash>
+ - <@parseValue value=key />=
+ <@hashMap map=value simpleMode=true />
+
+ <#else>
+ - <@parseValue value=key />=<@parseValue value=value />
+ #if>
+ #if>
+ <#assign index = index + 1 />
+ #list>
+
+#macro>
+
+<#-- renders an audit entry value -->
+<#macro parseValue value="null">
+ <#if value?is_number>
+ ${value?c}
+ <#elseif value?is_boolean>
+ ${value?string}
+ <#elseif value?is_date>
+ ${value?datetime}
+ <#elseif value?is_string && value != "null">
+ ${shortQName(value?string)}
+ <#elseif value?is_hash && value?values[0]?exists>
+ ${value?values[0]}
+ #if>
+#macro>
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/avm/wf/AVMDeployHandler.java b/source/java/org/alfresco/repo/avm/wf/AVMDeployHandler.java
index 80bc979e96..7f7f6a50f3 100644
--- a/source/java/org/alfresco/repo/avm/wf/AVMDeployHandler.java
+++ b/source/java/org/alfresco/repo/avm/wf/AVMDeployHandler.java
@@ -24,17 +24,15 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Properties;
import org.alfresco.config.JNDIConstants;
-import org.alfresco.model.ContentModel;
import org.alfresco.model.WCMAppModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.actions.AVMDeployWebsiteAction;
import org.alfresco.repo.domain.PropertyValue;
-import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.workflow.jbpm.JBPMNode;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
+import org.alfresco.service.cmr.avm.deploy.DeploymentService;
import org.alfresco.wcm.sandbox.SandboxConstants;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
@@ -42,13 +40,9 @@ import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
-import org.alfresco.service.cmr.search.ResultSet;
-import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PermissionService;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
-import org.alfresco.util.ISO9075;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -63,21 +57,17 @@ import org.springframework.beans.factory.BeanFactory;
*/
public class AVMDeployHandler extends JBPMSpringActionHandler
{
+ private DeploymentService deploymentService;
private AVMService avmService;
private ActionService actionService;
- private SearchService searchService;
private NodeService unprotectedNodeService;
private PermissionService unprotectedPermissionService;
- private ImporterBootstrap importerBootstrap;
+ private static final String BEAN_DEPLOYMENT_SERVICE = "deploymentService";
private static final String BEAN_AVM_SERVICE = "AVMService";
private static final String BEAN_ACTION_SERVICE = "actionService";
private static final String BEAN_NODE_SERVICE = "nodeService";
- private static final String BEAN_SEARCH_SERVICE = "searchService";
private static final String BEAN_PERMISSION_SERVICE = "permissionService";
- private static final String BEAN_IMPORTER_BOOTSTRAP = "spacesBootstrap";
- private static final String PROP_ROOT_FOLDER = "spaces.company_home.childname";
- private static final String PROP_WCM_FOLDER = "spaces.wcm.childname";
private static final long serialVersionUID = 5590265401983087178L;
private static final Log logger = LogFactory.getLog(AVMDeployHandler.class);
@@ -89,10 +79,9 @@ public class AVMDeployHandler extends JBPMSpringActionHandler
@Override
protected void initialiseHandler(BeanFactory factory)
{
+ this.deploymentService = (DeploymentService)factory.getBean(BEAN_DEPLOYMENT_SERVICE);
this.avmService = (AVMService)factory.getBean(BEAN_AVM_SERVICE);
this.actionService = (ActionService)factory.getBean(BEAN_ACTION_SERVICE);
- this.searchService = (SearchService)factory.getBean(BEAN_SEARCH_SERVICE);
- this.importerBootstrap = (ImporterBootstrap)factory.getBean(BEAN_IMPORTER_BOOTSTRAP);
this.unprotectedNodeService = (NodeService)factory.getBean(BEAN_NODE_SERVICE);
this.unprotectedPermissionService = (PermissionService)factory.getBean(BEAN_PERMISSION_SERVICE);
}
@@ -122,7 +111,7 @@ public class AVMDeployHandler extends JBPMSpringActionHandler
NodeRef webProjectRef = webProjNode.getNodeRef();
// get the list of live servers for the project that have the auto deploy flag turned on
- List servers = findDeployToServers(webProjectRef);
+ List servers = deploymentService.findLiveDeploymentServers(webProjectRef);
// if there are servers do the deploy
if (servers.size() > 0)
@@ -206,59 +195,4 @@ public class AVMDeployHandler extends JBPMSpringActionHandler
}
}
}
-
- private List findDeployToServers(NodeRef webProjectRef)
- {
- // get folder names
- Properties configuration = this.importerBootstrap.getConfiguration();
- String rootFolder = configuration.getProperty(PROP_ROOT_FOLDER);
- String wcmFolder = configuration.getProperty(PROP_WCM_FOLDER);
-
- // get web project name
- String webProjectName = (String)this.unprotectedNodeService.getProperty(
- webProjectRef, ContentModel.PROP_NAME);
- String safeProjectName = ISO9075.encode(webProjectName);
-
- // build the query
- StringBuilder query = new StringBuilder("PATH:\"/");
- query.append(rootFolder);
- query.append("/");
- query.append(wcmFolder);
- query.append("/cm:");
- query.append(safeProjectName);
- query.append("/*\" AND @");
- query.append(NamespaceService.WCMAPP_MODEL_PREFIX);
- query.append("\\:");
- query.append(WCMAppModel.PROP_DEPLOYSERVERTYPE.getLocalName());
- query.append(":\"");
- query.append(WCMAppModel.CONSTRAINT_LIVESERVER);
- query.append("\" AND @");
- query.append(NamespaceService.WCMAPP_MODEL_PREFIX);
- query.append("\\:");
- query.append(WCMAppModel.PROP_DEPLOYONAPPROVAL.getLocalName());
- query.append(":\"true\"");
-
- // execute the query
- ResultSet results = null;
- List servers = new ArrayList();
- try
- {
- results = searchService.query(webProjectRef.getStoreRef(),
- SearchService.LANGUAGE_LUCENE, query.toString());
-
- for (NodeRef server : results.getNodeRefs())
- {
- servers.add(server);
- }
- }
- finally
- {
- if (results != null)
- {
- results.close();
- }
- }
-
- return servers;
- }
}
diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java
index af81eccb38..98110b8e25 100644
--- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java
+++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java
@@ -38,7 +38,6 @@ import org.alfresco.repo.policy.BehaviourFilter;
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;
import org.alfresco.service.cmr.coci.CheckOutCheckInServiceException;
import org.alfresco.service.cmr.lock.LockService;
@@ -95,9 +94,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService
* Extension character, used to recalculate the working copy names
*/
private static final String EXTENSION_CHARACTER = ".";
-
+
private static Log logger = LogFactory.getLog(CheckOutCheckInServiceImpl.class);
-
+
private NodeService nodeService;
private VersionService versionService;
private LockService lockService;
@@ -108,12 +107,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService
private AuthenticationService authenticationService;
private RuleService ruleService;
- /**
- * The versionable aspect behaviour implementation
- */
- @SuppressWarnings("unused")
- private VersionableAspect versionableAspect;
-
+ /** Component to determine which behaviours are active and which not */
private BehaviourFilter behaviourFilter;
/**
@@ -180,14 +174,6 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService
this.fileFolderService = fileFolderService;
}
- /**
- * Sets the versionable aspect behaviour implementation
- */
- public void setVersionableAspect(VersionableAspect versionableAspect)
- {
- this.versionableAspect = versionableAspect;
- }
-
/**
* @param policyComponent policy component
*/
diff --git a/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java b/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java
index 4698f9eb4b..372a3ac3c3 100644
--- a/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java
+++ b/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java
@@ -151,7 +151,7 @@ public abstract class AbstractCopyBehaviourCallback implements CopyBehaviourCall
Serializable newListValue = oldListValue;
if (oldListValue instanceof NodeRef)
{
- newListValue = repointNodeRef(copyMap, (NodeRef) value);
+ newListValue = repointNodeRef(copyMap, (NodeRef) oldListValue);
}
// Put the value in the new list even though the new list might be discarded
newList.add(newListValue);
@@ -159,7 +159,7 @@ public abstract class AbstractCopyBehaviourCallback implements CopyBehaviourCall
if (!newListValue.equals(oldListValue))
{
// The value changed, so the new list will have to be set onto the target node
- newValue = newListValue;
+ newValue = (Serializable) newList;
}
}
}
diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java
index 3abbfe64d4..7c5daf15a0 100644
--- a/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java
+++ b/source/java/org/alfresco/repo/deploy/DeploymentServiceImpl.java
@@ -47,6 +47,7 @@ import org.alfresco.deployment.DeploymentToken;
import org.alfresco.deployment.DeploymentTransportOutputFilter;
import org.alfresco.deployment.FileDescriptor;
import org.alfresco.deployment.FileType;
+import org.alfresco.model.WCMAppModel;
import org.alfresco.repo.action.ActionServiceRemote;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.AVMNodeService;
@@ -79,7 +80,13 @@ import org.alfresco.service.cmr.remote.AVMRemoteTransport;
import org.alfresco.service.cmr.remote.AVMSyncServiceTransport;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.Path;
+import org.alfresco.service.cmr.search.ResultSet;
+import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.NameMatcher;
@@ -97,6 +104,10 @@ public class DeploymentServiceImpl implements DeploymentService
{
private static Log fgLogger = LogFactory.getLog(DeploymentServiceImpl.class);
+ private NodeService nodeService;
+ private NamespacePrefixResolver namespacePrefixResolver;
+ private SearchService searchService;
+
/**
* The local AVMService Instance.
*/
@@ -194,6 +205,33 @@ public class DeploymentServiceImpl implements DeploymentService
this.trxService = trxService;
}
+ /**
+ * Setter.
+ * @param nodeService The instance to set.
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * Setter.
+ * @param namespacePrefixResolver The instance to set.
+ */
+ public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver)
+ {
+ this.namespacePrefixResolver = namespacePrefixResolver;
+ }
+
+ /**
+ * Setter.
+ * @param searchService The instance to set.
+ */
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
/*
* Deploy differences to an ASR
* (non-Javadoc)
@@ -1634,6 +1672,83 @@ public class DeploymentServiceImpl implements DeploymentService
}
}
+ public List findLiveDeploymentServers(NodeRef webProjectRef)
+ {
+ return findDeploymentServers(webProjectRef, true, false);
+ }
+
+ public List findTestDeploymentServers(NodeRef webProjectRef, boolean availableOnly)
+ {
+ return findDeploymentServers(webProjectRef, false, availableOnly);
+ }
+
+ private List findDeploymentServers(NodeRef webProjectRef, boolean live, boolean availableOnly)
+ {
+
+ Path projectPath = nodeService.getPath(webProjectRef);
+ String stringPath = projectPath.toPrefixString(namespacePrefixResolver);
+ String serverType;
+
+ if (live)
+ {
+ serverType = WCMAppModel.CONSTRAINT_LIVESERVER;
+ }
+ else
+ {
+ serverType = WCMAppModel.CONSTRAINT_TESTSERVER;
+ }
+
+
+ StringBuilder query = new StringBuilder("PATH:\"");
+
+ query.append(stringPath);
+ query.append("/*\" ");
+ query.append(" AND @");
+ query.append(NamespaceService.WCMAPP_MODEL_PREFIX);
+ query.append("\\:");
+ query.append(WCMAppModel.PROP_DEPLOYSERVERTYPE.getLocalName());
+ query.append(":\"");
+ query.append(serverType);
+ query.append("\"");
+
+ // if required filter the test servers
+ if (live == false && availableOnly)
+ {
+ query.append(" AND ISNULL:\"");
+ query.append(WCMAppModel.PROP_DEPLOYSERVERALLOCATEDTO.toString());
+ query.append("\"");
+ }
+
+ if (fgLogger.isDebugEnabled())
+ fgLogger.debug("Finding deployment servers using query: " + query.toString());
+
+ // execute the query
+ ResultSet results = null;
+ List servers = new ArrayList();
+ try
+ {
+ results = searchService.query(webProjectRef.getStoreRef(),
+ SearchService.LANGUAGE_LUCENE, query.toString());
+
+ if (fgLogger.isDebugEnabled())
+ fgLogger.debug("Found " + results.length() + " deployment servers");
+
+ for (NodeRef server : results.getNodeRefs())
+ {
+ servers.add(server);
+ }
+ }
+ finally
+ {
+ if (results != null)
+ {
+ results.close();
+ }
+ }
+
+ return servers;
+ }
+
public void setNumberOfSendingThreads(int numberOfSendingThreads) {
this.numberOfSendingThreads = numberOfSendingThreads;
}
diff --git a/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java b/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java
index 439f633f3a..98ea6bfe3a 100644
--- a/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java
+++ b/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java
@@ -18,30 +18,47 @@
*/package org.alfresco.repo.exporter;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
-import org.springframework.extensions.surf.util.I18NUtil;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.search.CategoryService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.view.Exporter;
import org.alfresco.service.cmr.view.ExporterContext;
import org.alfresco.service.cmr.view.ExporterCrawlerParameters;
import org.alfresco.service.cmr.view.ExporterService;
+import org.alfresco.service.cmr.view.ImportPackageHandler;
import org.alfresco.service.cmr.view.ImporterService;
import org.alfresco.service.cmr.view.Location;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
import org.alfresco.util.TempFileProvider;
import org.alfresco.util.debug.NodeStoreInspector;
+import org.springframework.extensions.surf.util.I18NUtil;
public class ExporterComponentTest extends BaseSpringTest
@@ -50,6 +67,8 @@ public class ExporterComponentTest extends BaseSpringTest
private NodeService nodeService;
private ExporterService exporterService;
private ImporterService importerService;
+ private FileFolderService fileFolderService;
+ private CategoryService categoryService;
private StoreRef storeRef;
private AuthenticationComponent authenticationComponent;
@@ -60,18 +79,11 @@ public class ExporterComponentTest extends BaseSpringTest
nodeService = (NodeService)applicationContext.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
exporterService = (ExporterService)applicationContext.getBean("exporterComponent");
importerService = (ImporterService)applicationContext.getBean("importerComponent");
-
- // Create the store
-// this.storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
-// this.storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "test");
-// this.storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
-
-
+ fileFolderService = (FileFolderService) applicationContext.getBean("fileFolderService");
+ categoryService = (CategoryService) applicationContext.getBean("categoryService");
this.authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent");
-
this.authenticationComponent.setSystemUserAsCurrentUser();
-
this.storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
}
@@ -116,6 +128,167 @@ public class ExporterComponentTest extends BaseSpringTest
output.close();
}
+ /**
+ * Round-trip of export then import will result in the imported content having the same categories
+ * assigned to it as for the exported content -- provided the source and destination stores are the same.
+ */
+ @SuppressWarnings("unchecked")
+ public void testRoundTripKeepsCategoriesWhenWithinSameStore() throws Exception
+ {
+ // Use a store ref that has the bootstrapped categories
+ StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
+ NodeRef rootNode = nodeService.getRootNode(storeRef);
+
+ ChildAssociationRef contentChildAssocRef = createContentWithCategories(storeRef, rootNode);
+
+ // Export/import
+ File acpFile = exportContent(contentChildAssocRef);
+ FileInfo importFolderFileInfo = importContent(acpFile, rootNode);
+
+ // Check categories
+ NodeRef importedFileNode = fileFolderService.searchSimple(importFolderFileInfo.getNodeRef(), "test.txt");
+ assertNotNull("Couldn't find imported file: test.txt", importedFileNode);
+ assertTrue(nodeService.hasAspect(importedFileNode, ContentModel.ASPECT_GEN_CLASSIFIABLE));
+ List importedFileCategories = (List)
+ nodeService.getProperty(importedFileNode, ContentModel.PROP_CATEGORIES);
+ assertCategoriesEqual(importedFileCategories,
+ "Regions",
+ "Software Document Classification");
+ }
+
+ /**
+ * If the source and destination stores are not the same, then a round-trip of export then import
+ * will result in the imported content not having the categories assigned to it that were present
+ * on the exported content.
+ */
+ @SuppressWarnings("unchecked")
+ public void testRoundTripLosesCategoriesImportingToDifferentStore() throws Exception
+ {
+ // Use a store ref that has the bootstrapped categories
+ StoreRef storeRef = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
+ NodeRef rootNode = nodeService.getRootNode(storeRef);
+
+ ChildAssociationRef contentChildAssocRef = createContentWithCategories(storeRef, rootNode);
+
+ // Export
+ File acpFile = exportContent(contentChildAssocRef);
+ // Import - destination store is different from export store.
+ NodeRef destRootNode = nodeService.getRootNode(this.storeRef);
+ FileInfo importFolderFileInfo = importContent(acpFile, destRootNode);
+
+ // Check categories
+ NodeRef importedFileNode = fileFolderService.searchSimple(importFolderFileInfo.getNodeRef(), "test.txt");
+ assertNotNull("Couldn't find imported file: test.txt", importedFileNode);
+ assertTrue(nodeService.hasAspect(importedFileNode, ContentModel.ASPECT_GEN_CLASSIFIABLE));
+ List importedFileCategories = (List)
+ nodeService.getProperty(importedFileNode, ContentModel.PROP_CATEGORIES);
+ assertEquals("No categories should have been imported for the content", 0, importedFileCategories.size());
+ }
+
+ /**
+ * @param contentChildAssocRef
+ * @return
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private File exportContent(ChildAssociationRef contentChildAssocRef)
+ throws FileNotFoundException, IOException
+ {
+ TestProgress testProgress = new TestProgress();
+ Location location = new Location(contentChildAssocRef.getParentRef());
+ ExporterCrawlerParameters parameters = new ExporterCrawlerParameters();
+ parameters.setExportFrom(location);
+ File acpFile = TempFileProvider.createTempFile("category-export-test", ACPExportPackageHandler.ACP_EXTENSION);
+ System.out.println("Exporting to file: " + acpFile.getAbsolutePath());
+ File dataFile = new File("test-data-file");
+ File contentDir = new File("test-content-dir");
+ OutputStream fos = new FileOutputStream(acpFile);
+ ACPExportPackageHandler acpHandler = new ACPExportPackageHandler(fos, dataFile, contentDir, null);
+ acpHandler.setNodeService(nodeService);
+ acpHandler.setExportAsFolders(true);
+ exporterService.exportView(acpHandler, parameters, testProgress);
+ fos.close();
+ return acpFile;
+ }
+
+ /**
+ * @param storeRef
+ * @param rootNode
+ * @return
+ */
+ private ChildAssociationRef createContentWithCategories(StoreRef storeRef, NodeRef rootNode)
+ {
+ Collection assocRefs = categoryService.
+ getRootCategories(storeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE);
+ assertTrue("Pre-condition failure: not enough categories", assocRefs.size() >= 2);
+ Iterator it = assocRefs.iterator();
+ NodeRef softwareDocCategoryNode = it.next().getChildRef();
+ it.next(); // skip one
+ NodeRef regionsCategoryNode = it.next().getChildRef();
+
+
+ // Create a content node to categorise
+ FileInfo exportFileInfo = fileFolderService.create(rootNode, "Export Folder", ContentModel.TYPE_FOLDER);
+ Map properties = Collections.singletonMap(ContentModel.PROP_NAME, (Serializable)"test.txt");
+ ChildAssociationRef contentChildAssocRef = nodeService.createNode(
+ exportFileInfo.getNodeRef(),
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.ASSOC_CHILDREN,
+ ContentModel.TYPE_CONTENT,
+ properties);
+
+ NodeRef contentNodeRef = contentChildAssocRef.getChildRef();
+
+ // Attach categories
+ ArrayList categories = new ArrayList(2);
+ categories.add(softwareDocCategoryNode);
+ categories.add(regionsCategoryNode);
+ if(!nodeService.hasAspect(contentNodeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE))
+ {
+ HashMap props = new HashMap();
+ props.put(ContentModel.PROP_CATEGORIES, categories);
+ nodeService.addAspect(contentNodeRef, ContentModel.ASPECT_GEN_CLASSIFIABLE, props);
+ }
+ else
+ {
+ nodeService.setProperty(contentNodeRef, ContentModel.PROP_CATEGORIES, categories);
+ }
+ return contentChildAssocRef;
+ }
+
+ /**
+ * @param acpFile
+ * @param destRootNode
+ * @return
+ */
+ private FileInfo importContent(File acpFile, NodeRef destRootNode)
+ {
+ FileInfo importFolderFileInfo = fileFolderService.create(destRootNode, "Import Folder", ContentModel.TYPE_FOLDER);
+ ImportPackageHandler importHandler = new ACPImportPackageHandler(acpFile, ACPImportPackageHandler.DEFAULT_ENCODING);
+ importerService.importView(importHandler, new Location(importFolderFileInfo.getNodeRef()), null, null);
+ return importFolderFileInfo;
+ }
+
+ private void assertCategoriesEqual(List categories, String... expectedCategoryNames)
+ {
+ assertEquals("Number of categories is not as expected.", expectedCategoryNames.length, categories.size());
+
+ List categoryNames = new ArrayList(10);
+ for (NodeRef nodeRef : categories)
+ {
+ String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
+ categoryNames.add(name);
+ }
+ // Sort, to give deterministic test results
+ Collections.sort(categoryNames);
+
+ for (int i = 0; i < expectedCategoryNames.length; i++)
+ {
+ assertEquals(expectedCategoryNames[i], categoryNames.get(i));
+ }
+ }
+
+
private void dumpNodeStore(Locale locale)
{
diff --git a/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java b/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java
index 9c8e3e7222..830b2340cc 100644
--- a/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java
+++ b/source/java/org/alfresco/repo/exporter/ViewXMLExporter.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
import java.util.List;
import java.util.Locale;
+import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -560,7 +561,18 @@ import org.xml.sax.helpers.AttributesImpl;
NodeRef valueNodeRef = (NodeRef)value;
if (nodeRef.getStoreRef().equals(valueNodeRef.getStoreRef()))
{
- Path nodeRefPath = createPath(context.getExportParent(), nodeRef, valueNodeRef);
+ Path nodeRefPath = null;
+ if (property.equals(ContentModel.PROP_CATEGORIES))
+ {
+ // Special case for categories - use the full path so that categories
+ // can be successfully assigned to imported content (provided the same store
+ // was used for both import and export and the categories still exist).
+ nodeRefPath = nodeService.getPath(valueNodeRef);
+ }
+ else
+ {
+ nodeRefPath = createPath(context.getExportParent(), nodeRef, valueNodeRef);
+ }
value = (nodeRefPath == null) ? null : nodeRefPath.toPrefixString(namespaceService);
}
}
diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
index d321ea6ac5..f26e3d1ce4 100644
--- a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
+++ b/source/java/org/alfresco/repo/forms/FormServiceImplTest.java
@@ -1703,6 +1703,44 @@ public class FormServiceImplTest extends BaseAlfrescoSpringTest
assertNotNull(form);
}
+ public void testNonContentNode() throws Exception
+ {
+ // create a node (not cm:content)
+ Map nodeProps = new HashMap(1);
+ String nodeName = "testNode" + GUID.generate();
+ nodeProps.put(ContentModel.PROP_NAME, nodeName);
+ NodeRef node = this.nodeService.createNode(
+ this.folder,
+ ContentModel.ASSOC_CONTAINS,
+ QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeName),
+ ContentModel.TYPE_CMOBJECT,
+ nodeProps).getChildRef();
+
+ List fields = new ArrayList(8);
+ fields.add("cm:name");
+ fields.add("cm:creator");
+ fields.add("cm:created");
+
+ Form form = this.formService.getForm(new Item(NODE_FORM_ITEM_KIND, node.toString()), fields);
+
+ // check a form got returned
+ assertNotNull("Expecting form to be present", form);
+
+ // check fields were returned
+ List fieldNames = form.getFieldDefinitionNames();
+ assertEquals(3, fieldNames.size());
+
+ // rename the node via the forms service
+ FormData data = new FormData();
+ String newName = "new-" + nodeName;
+ data.addFieldData("prop_cm_name", newName);
+ this.formService.saveForm(new Item(NODE_FORM_ITEM_KIND, node.toString()), data);
+
+ Map updatedProps = this.nodeService.getProperties(node);
+ String updatedName = (String)updatedProps.get(ContentModel.PROP_NAME);
+ assertEquals(newName, updatedName);
+ }
+
public void testJavascriptAPI() throws Exception
{
Map model = new HashMap();
diff --git a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
index acc5f5ae77..4fb746c6cd 100644
--- a/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
+++ b/source/java/org/alfresco/repo/forms/processor/node/ContentModelFormProcessor.java
@@ -55,6 +55,7 @@ import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.model.FileExistsException;
import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -330,7 +331,7 @@ public abstract class ContentModelFormProcessor extends
// requirements
if (fullQName.equals(ContentModel.PROP_NAME))
{
- processNamePropertyPersist(nodeRef, fieldData);
+ processNamePropertyPersist(nodeRef, fieldData, propsToPersist);
}
else if (propDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
{
@@ -592,22 +593,35 @@ public abstract class ContentModelFormProcessor extends
*
* @param nodeRef The NodeRef to update the name for
* @param fieldData The data representing the new name value
+ * @param propsToPersist Map of properties to be persisted
*/
- protected void processNamePropertyPersist(NodeRef nodeRef, FieldData fieldData)
+ protected void processNamePropertyPersist(NodeRef nodeRef, FieldData fieldData,
+ Map propsToPersist)
{
- try
+ // determine whether the file folder service can handle the current node
+ FileInfo fileInfo = this.fileFolderService.getFileInfo(nodeRef);
+ if (fileInfo != null)
{
- // if the name property changes the rename method of the file folder
- // service should be called rather than updating the property directly
- this.fileFolderService.rename(nodeRef, (String) fieldData.getValue());
+ try
+ {
+ // if the name property changes the rename method of the file folder
+ // service should be called rather than updating the property directly
+ this.fileFolderService.rename(nodeRef, (String) fieldData.getValue());
+ }
+ catch (FileExistsException fee)
+ {
+ throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fee);
+ }
+ catch (FileNotFoundException fnne)
+ {
+ throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fnne);
+ }
}
- catch (FileExistsException fee)
+ else
{
- throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fee);
- }
- catch (FileNotFoundException fnne)
- {
- throw new FormException("Failed to persist field '" + fieldData.getName() + "'", fnne);
+ // as the file folder service can not be used just set the name property,
+ // the node service will deal with the details of renaming.
+ propsToPersist.put(ContentModel.PROP_NAME, (Serializable)fieldData.getValue());
}
}
diff --git a/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java
index 286c030d7b..80098f7ba1 100644
--- a/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java
+++ b/source/java/org/alfresco/repo/forms/processor/node/PropertyFieldProcessor.java
@@ -41,6 +41,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Period;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -148,6 +149,11 @@ public class PropertyFieldProcessor extends QNameFieldProcessor results = new ArrayList(10);
+ ArrayList results = new ArrayList(10);
// get the primary path
Path path = nodeService.getPath(nodeRef);
// iterate and turn the results into file info objects
@@ -1289,6 +1289,14 @@ public class FileFolderServiceImpl implements FileFolderService
}
// we found the root and expect to be building the path up
FileInfo pathInfo = toFileInfo(childNodeRef, true);
+
+ // we can't append a path element to the results if there is already a (non-folder) file at the tail
+ // since this would result in a path anomoly - file's cannot contain other files.
+ if (!results.isEmpty() && !results.get(results.size()-1).isFolder())
+ {
+ throw new InvalidTypeException(
+ "File is not the last element in path: files cannot contain other files.");
+ }
results.add(pathInfo);
}
// check that we found the root
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
index d0857bc0f3..c60be12de3 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java
@@ -41,6 +41,7 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.dictionary.M2Type;
+import org.alfresco.repo.model.filefolder.FileFolderServiceImpl.InvalidTypeException;
import org.alfresco.repo.node.integrity.IntegrityChecker;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -828,6 +829,68 @@ public class FileFolderServiceImplTest extends TestCase
}
}
+
+ public void testGetNamePathDoesNotReturnPathContainingNonLeafFileNode() throws Exception
+ {
+ FileInfo parentFolderInfo = getByName(NAME_L0_FOLDER_A, true);
+ assertNotNull(parentFolderInfo);
+ NodeRef parentFolderRef = parentFolderInfo.getNodeRef();
+
+ // create hierarchy: folder > file
+ FileInfo dirInfo = fileFolderService.create(parentFolderRef, "newDir", ContentModel.TYPE_FOLDER);
+ FileInfo fileInfo = fileFolderService.create(dirInfo.getNodeRef(), "newFile", ContentModel.TYPE_CONTENT);
+ // generate a path where the file is the last element: ok
+ List path = fileFolderService.getNamePath(parentFolderRef, fileInfo.getNodeRef());
+ assertEquals(2, path.size());
+
+
+ // create hierarchy: folder > file > file
+ FileInfo fileInfo2 = fileFolderService.create(fileInfo.getNodeRef(), "newFile2", ContentModel.TYPE_CONTENT);
+ // generate a path where a file is not the last element in the path: not ok
+ try
+ {
+ fileFolderService.getNamePath(parentFolderRef, fileInfo2.getNodeRef());
+ fail("Shouldn't create path for non-leaf file.");
+ }
+ catch(InvalidTypeException e)
+ {
+ // Good
+ }
+ }
+
+
+ public void testGetNamePathDoesNotCrossIntoNonFileFolderHierarchy() throws Exception
+ {
+ FileInfo parentFolderInfo = getByName(NAME_L0_FOLDER_A, true);
+ assertNotNull(parentFolderInfo);
+ NodeRef parentFolderRef = parentFolderInfo.getNodeRef();
+
+ // create hierarchy: folder > file
+ FileInfo dirInfo = fileFolderService.create(parentFolderRef, "newDir", ContentModel.TYPE_FOLDER);
+ FileInfo fileInfo = fileFolderService.create(dirInfo.getNodeRef(), "newFile", ContentModel.TYPE_CONTENT);
+ // generate a path where the file is the last element: ok
+ List path = fileFolderService.getNamePath(parentFolderRef, fileInfo.getNodeRef());
+ assertEquals(2, path.size());
+
+ NodeRef cmContainer = nodeService.createNode(
+ rootNodeRef,
+ ContentModel.ASSOC_CHILDREN,
+ QName.createQName(NamespaceService.ALFRESCO_URI, "container"),
+ ContentModel.TYPE_CONTAINER).getChildRef();
+
+ NodeRef cmChild = nodeService.moveNode(
+ fileInfo.getNodeRef(),
+ cmContainer,
+ ContentModel.ASSOC_CONTAINS,
+ QName.createQName(NamespaceService.ALFRESCO_URI, "contains")).getChildRef();
+
+ // This is ok, since the root - whilst not a folder - directly contains a file
+ List path2 = fileFolderService.getNamePath(cmContainer, cmChild);
+ assertEquals(1, path2.size());
+ assertEquals("newFile", path2.get(0).getName());
+ }
+
+
public void testSearchSimple() throws Exception
{
FileInfo folderInfo = getByName(NAME_L0_FOLDER_A, true);
diff --git a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java
index c10f28ec2d..f34e3389c5 100644
--- a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java
+++ b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java
@@ -78,6 +78,7 @@ public class MLTranslationInterceptor implements MethodInterceptor
METHOD_NAMES_SINGLE.add("searchSimple");
METHOD_NAMES_SINGLE.add("rename");
METHOD_NAMES_SINGLE.add("move");
+ METHOD_NAMES_SINGLE.add("moveFrom");
METHOD_NAMES_SINGLE.add("copy");
METHOD_NAMES_SINGLE.add("create");
METHOD_NAMES_SINGLE.add("makeFolders");
diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
index 8e2277e4ea..d742db2e14 100644
--- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
+++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java
@@ -19,7 +19,6 @@
package org.alfresco.repo.node;
import java.io.Serializable;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -98,7 +97,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
protected DictionaryService dictionaryService;
protected TransactionService transactionService;
protected TenantService tenantService;
- protected List storesToIgnorePolicies = new ArrayList(0);
+ protected Set storesToIgnorePolicies = Collections.emptySet();
/*
* Policy delegates
@@ -154,7 +153,7 @@ public abstract class AbstractNodeServiceImpl implements NodeService
this.tenantService = tenantService;
}
- public void setStoresToIgnorePolicies(List storesToIgnorePolicies)
+ public void setStoresToIgnorePolicies(Set storesToIgnorePolicies)
{
this.storesToIgnorePolicies = storesToIgnorePolicies;
}
diff --git a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java
index 41a5d05a61..3610f2a896 100644
--- a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java
+++ b/source/java/org/alfresco/repo/ownable/impl/OwnableServiceImpl.java
@@ -19,8 +19,10 @@
package org.alfresco.repo.ownable.impl;
import java.io.Serializable;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache;
@@ -33,13 +35,13 @@ import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.OwnableService;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.PropertyCheck;
@@ -61,6 +63,8 @@ public class OwnableServiceImpl implements
private AuthenticationService authenticationService;
private SimpleCache nodeOwnerCache;
private PolicyComponent policyComponent;
+ private TenantService tenantService;
+ private Set storesToIgnorePolicies = Collections.emptySet();
public OwnableServiceImpl()
{
@@ -84,6 +88,16 @@ public class OwnableServiceImpl implements
this.policyComponent = policyComponent;
}
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ public void setStoresToIgnorePolicies(Set storesToIgnorePolicies)
+ {
+ this.storesToIgnorePolicies = storesToIgnorePolicies;
+ }
+
/**
* @param ownerCache
* a transactionally-safe cache of node owners
@@ -164,7 +178,7 @@ public class OwnableServiceImpl implements
{
userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR));
}
- nodeOwnerCache.put(nodeRef, userName);
+ cacheOwner(nodeRef, userName);
}
return userName;
@@ -182,7 +196,7 @@ public class OwnableServiceImpl implements
{
nodeService.setProperty(nodeRef, ContentModel.PROP_OWNER, userName);
}
- nodeOwnerCache.put(nodeRef, userName);
+ cacheOwner(nodeRef, userName);
}
public void takeOwnership(NodeRef nodeRef)
@@ -241,6 +255,16 @@ public class OwnableServiceImpl implements
return AuditableOwnableAspectCopyBehaviourCallback.INSTANCE;
}
+ private void cacheOwner(NodeRef nodeRef, String userName)
+ {
+ // do not cache owners of nodes that are from stores that ignores policies
+ // to prevent mess in nodeOwnerCache
+ if (!storesToIgnorePolicies.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString()))
+ {
+ nodeOwnerCache.put(nodeRef, userName);
+ }
+ }
+
/**
* Extends the default copy behaviour to prevent copying of some ownable and
* auditable properties, but lets the aspects themselves go through.
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java
index 188ea2c239..a6ed2198ba 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java
@@ -386,7 +386,7 @@ public class ADMLuceneTest extends TestCase implements DictionaryListener
mlText.addValue(Locale.GERMAN, "banane");
mlText.addValue(new Locale("el"), "μπανάνα");
mlText.addValue(Locale.ITALIAN, "banana");
- mlText.addValue(new Locale("ja"), "バナナ");
+ mlText.addValue(new Locale("ja"), "?ナナ");
mlText.addValue(new Locale("ko"), "바나나");
mlText.addValue(new Locale("pt"), "banana");
mlText.addValue(new Locale("ru"), "банан");
@@ -4191,6 +4191,9 @@ public class ADMLuceneTest extends TestCase implements DictionaryListener
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/one//thirteen/fourteen\"", null);
assertEquals(1, results.length());
results.close();
+ results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/one//thirteen/fourteen/.\"", null);
+ assertEquals(1, results.length());
+ results.close();
results = searcher.query(rootNodeRef.getStoreRef(), "lucene", "PATH:\"/one//thirteen/fourteen//.\"", null);
assertEquals(1, results.length());
results.close();
@@ -6088,7 +6091,7 @@ public class ADMLuceneTest extends TestCase implements DictionaryListener
sp = new SearchParameters();
sp.addStore(rootNodeRef.getStoreRef());
sp.setLanguage("lucene");
- sp.setQuery("@" + LuceneQueryParser.escape(mlQName.toString()) + ":バナナ");
+ sp.setQuery("@" + LuceneQueryParser.escape(mlQName.toString()) + ":?ナナ");
sp.addLocale(new Locale("ja"));
results = searcher.query(sp);
assertEquals(1, results.length());
diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
index be4c6b18f9..62ab001d79 100644
--- a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
+++ b/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfo.java
@@ -1386,17 +1386,12 @@ public class IndexInfo implements IndexMonitor
s_logger.debug("Main index reader references = " + ((ReferenceCounting) mainIndexReader).getReferenceCount());
}
- // Prevent close calls from really closing the main reader
- return new FilterIndexReader(mainIndexReader)
- {
-
- @Override
- protected void doClose() throws IOException
- {
- in.decRef();
- }
-
- };
+ // ALF-10040: Wrap with a one-off CachingIndexReader (with cache disabled) so that LeafScorer behaves and passes through SingleFieldSelectors to the main index readers
+ IndexReader reader = ReferenceCountingReadOnlyIndexReaderFactory.createReader(MAIN_READER + GUID.generate(), mainIndexReader, false, config);
+ ReferenceCounting refCounting = (ReferenceCounting) reader;
+ reader.incRef();
+ refCounting.setInvalidForReuse();
+ return reader;
}
catch (RuntimeException e)
{
diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java
index af3937bb54..48c1d4ef90 100644
--- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java
+++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java
@@ -585,6 +585,14 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
.getSearchParameters()));
}
}
+ if (returnedObject.length() > 0)
+ {
+ //force prefetch before starting record time
+ boolean builkFetch = returnedObject.getBulkFetch();
+ returnedObject.setBulkFetch(false);
+ returnedObject.getNodeRef(returnedObject.length() - 1);
+ returnedObject.setBulkFetch(builkFetch);
+ }
// record the start time
long startTimeMillis = System.currentTimeMillis();
@@ -715,6 +723,14 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
}
}
+ if (returnedObject.length() > 0)
+ {
+ // force prefetch before starting record time
+ boolean builkFetch = returnedObject.getBulkFetch();
+ returnedObject.setBulkFetch(false);
+ returnedObject.getNodeRef(returnedObject.length() - 1);
+ returnedObject.setBulkFetch(builkFetch);
+ }
// record the start time
long startTimeMillis = System.currentTimeMillis();
diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java
index d2dc6083c6..89965ab0a1 100644
--- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java
+++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java
@@ -463,7 +463,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic
properties.put(ContentModel.PROP_TITLE, title);
properties.put(ContentModel.PROP_DESCRIPTION, description);
- final NodeRef siteNodeRef = AuthenticationUtil.runAsSystem(new RunAsWork() {
+ final NodeRef siteNodeRef = AuthenticationUtil.runAs(new RunAsWork() {
@Override
public NodeRef doWork() throws Exception {
return nodeService.createNode(
@@ -474,7 +474,7 @@ public class SiteServiceImpl extends AbstractLifecycleBean implements SiteServic
properties
).getChildRef();
}
- });
+ }, AuthenticationUtil.getSystemUserName());
// Make the new site a tag scope
this.taggingService.addTagScope(siteNodeRef);
diff --git a/source/java/org/alfresco/repo/template/TemplateNode.java b/source/java/org/alfresco/repo/template/TemplateNode.java
index 77cd1ea346..4c4717e1a4 100644
--- a/source/java/org/alfresco/repo/template/TemplateNode.java
+++ b/source/java/org/alfresco/repo/template/TemplateNode.java
@@ -22,15 +22,22 @@ import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
import java.util.HashSet;
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.admin.SysAdminParams;
import org.alfresco.repo.admin.SysAdminParamsImpl;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.audit.AuditQueryParameters;
+import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.repository.AssociationRef;
@@ -528,6 +535,77 @@ public class TemplateNode extends BasePermissionsNode implements NamespacePrefix
return new NodeSearchResultsMap(this, this.services);
}
+ // ------------------------------------------------------------------------------
+ // Audit API
+
+ /**
+ * @return a list of AuditInfo objects describing the Audit Trail for this node instance
+ */
+ public List getAuditTrail()
+ {
+ final List result = new ArrayList();
+
+ // create the callback for auditQuery method
+ final AuditQueryCallback callback = new AuditQueryCallback()
+ {
+ public boolean valuesRequired()
+ {
+ return true;
+ }
+
+ public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error)
+ {
+ throw new AlfrescoRuntimeException("Failed to retrieve audit data.", error);
+ }
+
+ public boolean handleAuditEntry(Long entryId, String applicationName, String user, long time,
+ Map values)
+ {
+ TemplateAuditInfo auditInfo = new TemplateAuditInfo(applicationName, user, time, values);
+ result.add(auditInfo);
+ return true;
+ }
+ };
+
+ // resolve the path of the node
+ final String nodePath = services.getNodeService().getPath(this.nodeRef).toPrefixString(services.getNamespaceService());
+
+ // run as admin user to allow everyone to see audit information
+ // (new 3.4 API doesn't allow this by default)
+ AuthenticationUtil.runAs(new RunAsWork