diff --git a/config/alfresco/cmis-ws-context.xml b/config/alfresco/cmis-ws-context.xml
index 2c2023417e..340c38b15b 100644
--- a/config/alfresco/cmis-ws-context.xml
+++ b/config/alfresco/cmis-ws-context.xml
@@ -5,7 +5,7 @@
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
-
+
diff --git a/config/alfresco/cxf/cxf.xml b/config/alfresco/cxf/cxf.xml
new file mode 100644
index 0000000000..182b0f6d18
--- /dev/null
+++ b/config/alfresco/cxf/cxf.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/remote-api-context.xml b/config/alfresco/remote-api-context.xml
index 4677a0cd40..c38e96e298 100644
--- a/config/alfresco/remote-api-context.xml
+++ b/config/alfresco/remote-api-context.xml
@@ -25,10 +25,51 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${system.webdav.renameShufflePattern}
+
+
+ ${system.webdav.url.path.prefix}
+
+
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.lib.ftl
index 74d3a152ac..4532285dbf 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.lib.ftl
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comment.lib.ftl
@@ -43,7 +43,7 @@
"edit": false,
"delete": false
<#else>
- "edit": ${item.node.hasPermission("Write")?string},
+ "edit": ${item.canEditComment?string},
"delete": ${item.node.hasPermission("Delete")?string}
#if>
}
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js b/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js
index b2cd129299..cf54fa8a53 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/comments/comments.lib.js
@@ -63,6 +63,8 @@ function getCommentData(node)
data.node = node;
data.author = people.getPerson(node.properties["cm:creator"]);
data.isUpdated = (node.properties["cm:modified"] - node.properties["cm:created"]) > 5000;
+ data.canEditComment = (person == data.owner) || (person == data.author) ||
+ node.hasPermission("SiteManager") || node.hasPermission("Coordinator");
return data;
}
diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml
index b88af63776..b81f1f34f7 100644
--- a/config/alfresco/web-scripts-application-context.xml
+++ b/config/alfresco/web-scripts-application-context.xml
@@ -1532,6 +1532,7 @@
+
transformers = contentService.getActiveTransformers(reader.getMimetype(), sourceSize, MimetypeMap.MIMETYPE_TEXT_PLAIN, options);
+ transformerDebug.availableTransformers(transformers, sourceSize, "NodeContentGet");
- if(transformException == null)
- {
- // point the reader to the new-written content
- textReader = writer.getReader();
- // Check that the reader is a view onto something concrete
- if (textReader == null || !textReader.exists())
+ if (transformers.isEmpty())
{
- transformException = new ContentIOException(
- "The transformation did not write any content, yet: \n"
- + " transformer: " + transformer + "\n" + " temp writer: " + writer);
+ res.setHeader(TRANSFORM_STATUS_HEADER, "noTransform");
+ res.setStatus(HttpStatus.SC_NO_CONTENT);
+ return;
+ }
+ ContentTransformer transformer = transformers.get(0);
+
+ // Perform transformation catering for mimetype AND encoding
+ ContentWriter writer = contentService.getTempWriter();
+ writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
+ writer.setEncoding("UTF-8"); // Expect transformers to produce UTF-8
+
+ try
+ {
+ long start = System.currentTimeMillis();
+ transformer.transform(reader, writer);
+ long transformDuration = System.currentTimeMillis() - start;
+ res.setHeader(TRANSFORM_DURATION_HEADER, String.valueOf(transformDuration));
+ }
+ catch (ContentIOException e)
+ {
+ transformException = e;
+ }
+
+ if(transformException == null)
+ {
+ // point the reader to the new-written content
+ textReader = writer.getReader();
+ // Check that the reader is a view onto something concrete
+ if (textReader == null || !textReader.exists())
+ {
+ transformException = new ContentIOException(
+ "The transformation did not write any content, yet: \n"
+ + " transformer: " + transformer + "\n" + " temp writer: " + writer);
+ }
+ }
+
+ if(transformException != null)
+ {
+ res.setHeader(TRANSFORM_STATUS_HEADER, "transformFailed");
+ res.setHeader(TRANSFORM_EXCEPTION_HEADER, transformException.getMessage());
+ res.setStatus(HttpStatus.SC_NO_CONTENT);
+ }
+ else
+ {
+ res.setStatus(HttpStatus.SC_OK);
+ streamContentImpl(req, res, textReader, false, modified, String.valueOf(modified.getTime()), null);
}
}
-
- if(transformException != null)
+ finally
{
- res.setHeader(TRANSFORM_STATUS_HEADER, "transformFailed");
- res.setHeader(TRANSFORM_EXCEPTION_HEADER, transformException.getMessage());
- res.setStatus(HttpStatus.SC_NO_CONTENT);
- }
- else
- {
- res.setStatus(HttpStatus.SC_OK);
- streamContentImpl(req, res, textReader, false, modified, String.valueOf(modified.getTime()), null);
+ transformerDebug.popAvailable();
}
}
}
diff --git a/source/java/org/alfresco/repo/webdav/AbstractMoveOrCopyMethod.java b/source/java/org/alfresco/repo/webdav/AbstractMoveOrCopyMethod.java
deleted file mode 100644
index 4eb22194be..0000000000
--- a/source/java/org/alfresco/repo/webdav/AbstractMoveOrCopyMethod.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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.webdav;
-
-import javax.servlet.http.HttpServletResponse;
-
-import org.alfresco.model.ContentModel;
-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.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
-
-/**
- * Implements the WebDAV COPY and MOVE methods
- *
- * @author Derek Hulley
- */
-public abstract class AbstractMoveOrCopyMethod extends HierarchicalMethod
-{
- /**
- * Default constructor
- */
- public AbstractMoveOrCopyMethod()
- {
- }
-
- /**
- * Implement the move or copy, depending on the implementation
- *
- * @param fileFolderService the service to do the work
- * @param sourceNodeRef the node to copy or move
- * @param destParentNodeRef the destination parent
- * @param name the name of the file or folder
- * @throws Exception
- */
- protected abstract void moveOrCopy(
- FileFolderService fileFolderService,
- NodeRef sourceNodeRef,
- NodeRef sourceParentNodeRef,
- NodeRef destParentNodeRef,
- String name) throws Exception;
-
- /**
- * Exceute the request
- *
- * @exception WebDAVServerException
- */
- protected final void executeImpl() throws WebDAVServerException, Exception
- {
- FileFolderService fileFolderService = getFileFolderService();
-
- NodeRef rootNodeRef = getRootNodeRef();
- String servletPath = getServletPath();
-
- // Debug
- if (logger.isDebugEnabled())
- {
- logger.debug("Copy from " + getPath() + " to " + getDestinationPath());
- }
-
- // the source must exist
- String sourcePath = getPath();
- FileInfo sourceInfo = null;
- try
- {
- sourceInfo = getDAVHelper().getNodeForPath(rootNodeRef, sourcePath, servletPath);
- }
- catch (FileNotFoundException e)
- {
- throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
- }
-
- FileInfo sourceParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, sourcePath, servletPath);
-
- // the destination parent must exist
- String destPath = getDestinationPath();
- FileInfo destParentInfo = null;
- try
- {
- if (destPath.endsWith(WebDAVHelper.PathSeperator))
- {
- destPath = destPath.substring(0, destPath.length() - 1);
- }
- destParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, destPath, servletPath);
- }
- catch (FileNotFoundException e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Destination parent folder doesn't exist: " + destPath);
- }
- throw new WebDAVServerException(HttpServletResponse.SC_CONFLICT);
- }
-
- // check for the existence of the destination node
- FileInfo destInfo = null;
- try
- {
- destInfo = getDAVHelper().getNodeForPath(rootNodeRef, destPath, servletPath);
- if (!destInfo.getNodeRef().equals(sourceInfo.getNodeRef()))
- {
- // ALF-7079 fix, if destInfo is working copy then content will be updated later
- boolean isDestWorkingCopy = getNodeService().hasAspect(destInfo.getNodeRef(), ContentModel.ASPECT_WORKING_COPY);
- if (!hasOverWrite() && !isDestWorkingCopy)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug("Destination exists but overwrite is not allowed");
- }
- // it exists and we may not overwrite
- throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
- }
- // delete the destination node if it is not the same as the source node and not a working copy
- if (!isDestWorkingCopy && !isShuffleOperation(sourceInfo) && !isVersioned(destInfo))
- {
- checkNode(destInfo);
-
- // attempting to move or copy onto another node
- fileFolderService.delete(destInfo.getNodeRef());
- }
- }
- }
- catch (FileNotFoundException e)
- {
- // destination doesn't exist
- }
-
- NodeRef sourceNodeRef = sourceInfo.getNodeRef();
- NodeRef sourceParentNodeRef = sourceParentInfo.getNodeRef();
- NodeRef destParentNodeRef = destParentInfo.getNodeRef();
-
- String name = getDAVHelper().splitPath(destPath)[1];
-
- moveOrCopy(fileFolderService, sourceNodeRef, sourceParentNodeRef, destParentNodeRef, name);
-
- // Set the response status
- if (destInfo == null)
- {
- m_response.setStatus(HttpServletResponse.SC_CREATED);
- }
- else
- {
- m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
- }
- }
-
-
- protected boolean isVersioned(FileInfo fileInfo)
- {
- NodeService nodeService = getNodeService();
- boolean versioned = nodeService.hasAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_VERSIONABLE);
- return versioned;
- }
-
- protected boolean isShuffleOperation(FileInfo sourceInfo)
- {
- NodeService nodeService = getNodeService();
- boolean hidden = nodeService.hasAspect(sourceInfo.getNodeRef(), ContentModel.ASPECT_HIDDEN);
- boolean temporary = nodeService.hasAspect(sourceInfo.getNodeRef(), ContentModel.ASPECT_TEMPORARY);
- boolean shuffleOperation = hidden && temporary;
- return shuffleOperation;
- }
-
- protected void parseRequestHeaders() throws WebDAVServerException
- {
- super.parseRequestHeaders();
- parseIfHeader();
- }
-}
diff --git a/source/java/org/alfresco/repo/webdav/CopyMethod.java b/source/java/org/alfresco/repo/webdav/CopyMethod.java
index d5c5313e43..4779fc8437 100644
--- a/source/java/org/alfresco/repo/webdav/CopyMethod.java
+++ b/source/java/org/alfresco/repo/webdav/CopyMethod.java
@@ -26,7 +26,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
*
* @author Derek Hulley
*/
-public class CopyMethod extends AbstractMoveOrCopyMethod
+public class CopyMethod extends MoveMethod
{
/**
* Default constructor
@@ -35,14 +35,13 @@ public class CopyMethod extends AbstractMoveOrCopyMethod
{
}
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.webdav.MoveMethod#isMove()
+ */
@Override
- protected void moveOrCopy(
- FileFolderService fileFolderService,
- NodeRef sourceNodeRef,
- NodeRef sourceParentNodeRef,
- NodeRef destParentNodeRef,
- String name) throws Exception
+ protected boolean isMove()
{
- fileFolderService.copy(sourceNodeRef, destParentNodeRef, name);
+ return false;
}
}
diff --git a/source/java/org/alfresco/repo/webdav/DeleteMethod.java b/source/java/org/alfresco/repo/webdav/DeleteMethod.java
index 5ba8e272db..c2dbb3b48f 100644
--- a/source/java/org/alfresco/repo/webdav/DeleteMethod.java
+++ b/source/java/org/alfresco/repo/webdav/DeleteMethod.java
@@ -29,6 +29,7 @@ import org.alfresco.service.cmr.model.FileNotFoundException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.webdav.WebDavService;
+import org.alfresco.service.cmr.repository.NodeService;
/**
* Implements the WebDAV DELETE method
@@ -101,18 +102,30 @@ public class DeleteMethod extends WebDAVMethod implements ActivityPostProducer
checkNode(fileInfo);
- // ALF-7079 fix, working copies are not deleted at all
- if (!getNodeService().hasAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_WORKING_COPY))
+ NodeService nodeService = getNodeService();
+ NodeRef nodeRef = fileInfo.getNodeRef();
+ // MNT-181: working copies and versioned nodes are hidden rather than deleted
+ if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) || nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE))
+ {
+ setHidden(nodeRef, true);
+ getDAVLockService().unlock(nodeRef);
+ }
+ // We just ensure already-hidden nodes are left unlocked
+ else if (isHidden(nodeRef))
+ {
+ getDAVLockService().unlock(nodeRef);
+ }
+ // A 'real' delete
+ else
{
// As this content will be deleted, we need to extract some info before it's no longer available.
String siteId = getSiteId();
NodeRef deletedNodeRef = fileInfo.getNodeRef();
- FileInfo parentFile = getDAVHelper().getParentNodeForPath(getRootNodeRef(), getPath(), getServletPath());
- boolean hidden = fileInfo.isHidden();
+ FileInfo parentFile = getDAVHelper().getParentNodeForPath(getRootNodeRef(), path, getServletPath());
// Delete it
fileFolderService.delete(deletedNodeRef);
// Don't post activity data for hidden files, resource forks etc.
- if (!hidden)
+ if (!getDAVHelper().isRenameShuffle(path))
{
postActivity(parentFile, fileInfo, siteId);
}
diff --git a/source/java/org/alfresco/repo/webdav/MoveMethod.java b/source/java/org/alfresco/repo/webdav/MoveMethod.java
index e206000319..c15853f2e1 100644
--- a/source/java/org/alfresco/repo/webdav/MoveMethod.java
+++ b/source/java/org/alfresco/repo/webdav/MoveMethod.java
@@ -31,7 +31,6 @@ import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.repository.NodeService;
import org.dom4j.DocumentHelper;
import org.dom4j.io.XMLWriter;
import org.xml.sax.Attributes;
@@ -40,8 +39,9 @@ import org.xml.sax.Attributes;
* Implements the WebDAV MOVE method
*
* @author Derek Hulley
+ * @author David Ward
*/
-public class MoveMethod extends AbstractMoveOrCopyMethod
+public class MoveMethod extends HierarchicalMethod
{
/**
* Default constructor
@@ -49,14 +49,120 @@ public class MoveMethod extends AbstractMoveOrCopyMethod
public MoveMethod()
{
}
+
+ protected boolean isMove()
+ {
+ return true;
+ }
+
+ /**
+ * Exceute the request
+ *
+ * @exception WebDAVServerException
+ */
+ protected final void executeImpl() throws WebDAVServerException, Exception
+ {
+ NodeRef rootNodeRef = getRootNodeRef();
+ String servletPath = getServletPath();
+ // Debug
+ if (logger.isDebugEnabled())
+ {
+ logger.debug((isMove() ? "Move" : "Copy") + " from " + getPath() + " to " + getDestinationPath());
+ }
+
+ // the source must exist
+ String sourcePath = getPath();
+ FileInfo sourceInfo = null;
+ try
+ {
+ sourceInfo = getDAVHelper().getNodeForPath(rootNodeRef, sourcePath, servletPath);
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
+ }
+
+ FileInfo sourceParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, sourcePath, servletPath);
+
+ // the destination parent must exist
+ String destPath = getDestinationPath();
+ FileInfo destParentInfo = null;
+ try
+ {
+ if (destPath.endsWith(WebDAVHelper.PathSeperator))
+ {
+ destPath = destPath.substring(0, destPath.length() - 1);
+ }
+ destParentInfo = getDAVHelper().getParentNodeForPath(rootNodeRef, destPath, servletPath);
+ }
+ catch (FileNotFoundException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Destination parent folder doesn't exist: " + destPath);
+ }
+ throw new WebDAVServerException(HttpServletResponse.SC_CONFLICT);
+ }
+
+ // check for the existence of the destination node
+ FileInfo destInfo = null;
+ boolean destExists = false;
+ try
+ {
+ destInfo = getDAVHelper().getNodeForPath(rootNodeRef, destPath, servletPath);
+ if (!destInfo.getNodeRef().equals(sourceInfo.getNodeRef()))
+ {
+ // ALF-7079 fix, if destInfo is a hidden shuffle target then pretend it's not there
+ destExists = !isHidden(destInfo.getNodeRef());
+ if (!hasOverWrite() && destExists)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Destination exists but overwrite is not allowed");
+ }
+ // it exists and we may not overwrite
+ throw new WebDAVServerException(HttpServletResponse.SC_PRECONDITION_FAILED);
+ }
+ checkNode(destInfo);
+ }
+ }
+ catch (FileNotFoundException e)
+ {
+ // destination doesn't exist
+ }
+
+ NodeRef sourceNodeRef = sourceInfo.getNodeRef();
+ NodeRef sourceParentNodeRef = sourceParentInfo.getNodeRef();
+ NodeRef destParentNodeRef = destParentInfo.getNodeRef();
+
+ String name = getDAVHelper().splitPath(destPath)[1];
+
+ moveOrCopy(sourceNodeRef, sourceParentNodeRef, destParentNodeRef, name);
+
+ // Set the response status
+ if (!destExists)
+ {
+ m_response.setStatus(HttpServletResponse.SC_CREATED);
+ }
+ else
+ {
+ m_response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+ }
+ }
+
+ protected void parseRequestHeaders() throws WebDAVServerException
+ {
+ super.parseRequestHeaders();
+ parseIfHeader();
+ }
protected void moveOrCopy(
- FileFolderService fileFolderService,
NodeRef sourceNodeRef,
NodeRef sourceParentNodeRef,
NodeRef destParentNodeRef,
String name) throws Exception
{
+ FileFolderService fileFolderService = getFileFolderService();
NodeRef rootNodeRef = getRootNodeRef();
String sourcePath = getPath();
@@ -67,8 +173,8 @@ public class MoveMethod extends AbstractMoveOrCopyMethod
List destPathElements = getDAVHelper().splitAllPaths(destPath);
FileInfo destFileInfo = null;
- NodeService nodeService = getNodeService();
-
+ boolean isMove = isMove();
+
try
{
// get the node to move
@@ -87,12 +193,40 @@ public class MoveMethod extends AbstractMoveOrCopyMethod
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
}
}
+ if (isMove)
+ {
+ checkNode(sourceFileInfo);
+ }
+ // ALF-7079 fix, if destination exists then its content is updated with source content and source is deleted if
+ // this is a move
+ if (destFileInfo != null)
+ {
+ copyContentOnly(sourceNodeRef, destFileInfo, fileFolderService);
+ setHidden(destFileInfo.getNodeRef(), false);
+ if (isMove)
+ {
+ fileFolderService.delete(sourceNodeRef);
+ }
+ }
+ // If this is a copy then the source is just copied to destination.
+ else if (!isMove)
+ {
+ fileFolderService.copy(sourceNodeRef, destParentNodeRef, name);
+ }
+ // If this is a move and the destination looks like the start of a shuffle operation, then the source is just
+ // copied to destination and the source is hidden.
+ else if (getDAVHelper().isRenameShuffle(destPath) && !getDAVHelper().isRenameShuffle(sourcePath))
+ {
+ destFileInfo = fileFolderService.create(destParentNodeRef, name, ContentModel.TYPE_CONTENT);
+ copyContentOnly(sourceNodeRef, destFileInfo, fileFolderService);
+ setHidden(sourceNodeRef, true);
- checkNode(sourceFileInfo);
-
- if (sourceParentNodeRef.equals(destParentNodeRef))
+ // As per the WebDAV spec, we make sure the node is unlocked once moved
+ getDAVHelper().getLockService().unlock(sourceNodeRef);
+ }
+ else if (sourceParentNodeRef.equals(destParentNodeRef))
{
- // It is rename method
+ // It is a simple rename operation
try
{
fileFolderService.rename(sourceNodeRef, name);
@@ -111,43 +245,23 @@ public class MoveMethod extends AbstractMoveOrCopyMethod
xml.endElement(WebDAV.DAV_NS, WebDAV.XML_ERROR, WebDAV.XML_NS_ERROR);
m_response.setStatus(HttpServletResponse.SC_CONFLICT);
- return;
}
}
- else if (destFileInfo != null && (isShuffleOperation(sourceFileInfo) || isVersioned(destFileInfo)))
- {
- copyOnlyContent(sourceNodeRef, destFileInfo, fileFolderService);
- }
- else
- // ALF-7079 fix, if source is working copy then it is just copied to destination
- if (nodeService.hasAspect(sourceNodeRef, ContentModel.ASPECT_WORKING_COPY))
- {
- // replace move with copy action for working copies
- fileFolderService.copy(sourceNodeRef, destParentNodeRef, name);
- }
- // ALF-7079 fix, if destination exists and is working copy then its content is updated with
- // source content and source is deleted
- else if (destFileInfo != null && nodeService.hasAspect(destFileInfo.getNodeRef(), ContentModel.ASPECT_WORKING_COPY))
- {
- // copy only content for working copy destination
- copyOnlyContent(sourceNodeRef, destFileInfo, fileFolderService);
- }
else
{
// It is a simple move operation
fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, destParentNodeRef, name);
+
+ // As per the WebDAV spec, we make sure the node is unlocked once moved
+ getDAVHelper().getLockService().unlock(sourceNodeRef);
}
- // As per the WebDAV spec, we make sure the node is unlocked once moved
- getDAVHelper().getLockService().unlock(sourceNodeRef);
}
- private void copyOnlyContent(NodeRef sourceNodeRef, FileInfo destFileInfo, FileFolderService fileFolderService)
+ private void copyContentOnly(NodeRef sourceNodeRef, FileInfo destFileInfo, FileFolderService fileFolderService)
{
ContentService contentService = getContentService();
ContentReader reader = contentService.getReader(sourceNodeRef, ContentModel.PROP_CONTENT);
ContentWriter contentWriter = contentService.getWriter(destFileInfo.getNodeRef(), ContentModel.PROP_CONTENT, true);
contentWriter.putContent(reader);
-
- fileFolderService.delete(sourceNodeRef);
}
}
diff --git a/source/java/org/alfresco/repo/webdav/PropFindMethod.java b/source/java/org/alfresco/repo/webdav/PropFindMethod.java
index 921c987e76..3cebb73a24 100644
--- a/source/java/org/alfresco/repo/webdav/PropFindMethod.java
+++ b/source/java/org/alfresco/repo/webdav/PropFindMethod.java
@@ -36,7 +36,6 @@ 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.ContentData;
-import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.repository.datatype.TypeConverter;
import org.alfresco.service.namespace.QName;
@@ -195,6 +194,13 @@ public class PropFindMethod extends WebDAVMethod
// The path is not valid - send a 404 error back to the client
throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
}
+
+ // A node hidden during a 'shuffle' operation - send a 404 error back to the client, as some Mac clients need
+ // this
+ if (isHidden(pathNodeInfo.getNodeRef()))
+ {
+ throw new WebDAVServerException(HttpServletResponse.SC_NOT_FOUND);
+ }
// Set the response content type
diff --git a/source/java/org/alfresco/repo/webdav/PutMethod.java b/source/java/org/alfresco/repo/webdav/PutMethod.java
index e696f334b7..8e88ce213e 100644
--- a/source/java/org/alfresco/repo/webdav/PutMethod.java
+++ b/source/java/org/alfresco/repo/webdav/PutMethod.java
@@ -164,7 +164,14 @@ public class PutMethod extends WebDAVMethod implements ActivityPostProducer
}
checkNode(contentNodeInfo);
-
+
+ // 'Unhide' hidden nodes and behave as though we created them
+ NodeRef contentNodeRef = contentNodeInfo.getNodeRef();
+ if (isHidden(contentNodeRef))
+ {
+ setHidden(contentNodeRef, false);
+ created = true;
+ }
}
catch (FileNotFoundException e)
{
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
index f4610f0d23..e92b37bcf7 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVHelper.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
+import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -34,10 +35,12 @@ import javax.servlet.http.HttpServletResponse;
import org.alfresco.jlan.util.IPAddress;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.lock.LockUtils;
+import org.alfresco.repo.model.filefolder.HiddenAspect;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
@@ -52,7 +55,6 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.namespace.NamespaceService;
-import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.alfresco.util.Pair;
import org.apache.commons.lang.NotImplementedException;
@@ -73,9 +75,13 @@ import org.xml.sax.helpers.AttributesImpl;
public class WebDAVHelper
{
// Constants
+
+ public static final String BEAN_NAME = "webDAVHelper";
+
private static final String HTTPS_SCHEME = "https://";
private static final String HTTP_SCHEME = "http://";
+
// Path seperator
public static final String PathSeperator = "/";
public static final char PathSeperatorChar = '/';
@@ -84,7 +90,7 @@ public class WebDAVHelper
// Logging
private static Log logger = LogFactory.getLog("org.alfresco.webdav.protocol");
- // Service registry
+ // Service registry TODO: eliminate this - not dependency injection!
private ServiceRegistry m_serviceRegistry;
// Services
@@ -99,33 +105,30 @@ public class WebDAVHelper
private AuthenticationService m_authService;
private PermissionService m_permissionService;
private TenantService m_tenantService;
+ private HiddenAspect m_hiddenAspect;
+
+ // pattern is tested against full path after it has been lower cased.
+ private Pattern m_renameShufflePattern = Pattern.compile("(.*/\\..*)|(.*[a-f0-9]{8}+$)|(.*\\.tmp$)|(.*\\.wbk$)|(.*\\.bak$)|(.*\\~$)");
// Empty XML attribute list
- private AttributesImpl m_nullAttribs = new AttributesImpl();
+ private final AttributesImpl m_nullAttribs = new AttributesImpl();
private String m_urlPathPrefix;
-
- /**
- * Class constructor
- */
- protected WebDAVHelper(String urlPathPrefix, ServiceRegistry serviceRegistry, AuthenticationService authService, TenantService tenantService)
- {
- m_serviceRegistry = serviceRegistry;
- m_nodeService = m_serviceRegistry.getNodeService();
- m_fileFolderService = m_serviceRegistry.getFileFolderService();
- m_searchService = m_serviceRegistry.getSearchService();
- m_namespaceService = m_serviceRegistry.getNamespaceService();
- m_dictionaryService = m_serviceRegistry.getDictionaryService();
- m_mimetypeService = m_serviceRegistry.getMimetypeService();
- m_lockService = (WebDAVLockService)m_serviceRegistry.getService(QName.createQName(NamespaceService.ALFRESCO_URI, WebDAVLockService.BEAN_NAME));
- m_actionService = m_serviceRegistry.getActionService();
- m_permissionService = m_serviceRegistry.getPermissionService();
- m_tenantService = tenantService;
- m_authService = authService;
- m_urlPathPrefix = urlPathPrefix;
+ /**
+ * Set the regular expression that will be applied to filenames during renames
+ * to detect whether clients are performing a renaming shuffle - common during
+ * file saving on various clients.
+ *
+ *
+ *
+ * @param renameShufflePattern a regular expression filename match
+ */
+ public void setRenameShufflePattern(Pattern renameShufflePattern)
+ {
+ this.m_renameShufflePattern = renameShufflePattern;
}
-
+
/**
* @return Return the authentication service
*/
@@ -139,6 +142,7 @@ public class WebDAVHelper
*/
public final ServiceRegistry getServiceRegistry()
{
+ // TODO: eliminate this - not dependency injection!
return m_serviceRegistry;
}
@@ -211,7 +215,15 @@ public class WebDAVHelper
{
return m_permissionService;
}
-
+
+ /**
+ * @return the hidden aspect bean
+ */
+ public final HiddenAspect getHiddenAspect()
+ {
+ return m_hiddenAspect;
+ }
+
/**
* Retrieve the {@link TenantService} held by the helper.
*
@@ -230,6 +242,118 @@ public class WebDAVHelper
return getServiceRegistry().getCopyService();
}
+ public void setTenantService(TenantService tenantService)
+ {
+ this.m_tenantService = tenantService;
+ }
+
+ /**
+ * @param serviceRegistry the service registry
+ */
+ public void setServiceRegistry(ServiceRegistry serviceRegistry)
+ {
+ this.m_serviceRegistry = serviceRegistry;
+ }
+
+ /**
+ * @param nodeService the node service
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.m_nodeService = nodeService;
+ }
+
+ /**
+ * @param fileFolderService the fileFolder service
+ */
+ public void setFileFolderService(FileFolderService fileFolderService)
+ {
+ this.m_fileFolderService = fileFolderService;
+ }
+
+ /**
+ * @param searchService the search service
+ */
+ public void setSearchService(SearchService searchService)
+ {
+ this.m_searchService = searchService;
+ }
+
+ /**
+ * @param namespaceService the namespace service
+ */
+ public void setNamespaceService(NamespaceService namespaceService)
+ {
+ this.m_namespaceService = namespaceService;
+ }
+
+ /**
+ * @param dictionaryService the dictionary service
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.m_dictionaryService = dictionaryService;
+ }
+
+ /**
+ * @param mimetypeService the mimetype service
+ */
+ public void setMimetypeService(MimetypeService mimetypeService)
+ {
+ this.m_mimetypeService = mimetypeService;
+ }
+
+ /**
+ * @param lockService the lock service
+ */
+ public void setLockService(WebDAVLockService lockService)
+ {
+ this.m_lockService = lockService;
+ }
+
+ /**
+ * @param actionService the action service
+ */
+ public void setActionService(ActionService actionService)
+ {
+ this.m_actionService = actionService;
+ }
+
+ /**
+ * @param authService the authentication service
+ */
+ public void setAuthenticationService(AuthenticationService authService)
+ {
+ this.m_authService = authService;
+ }
+
+ /**
+ * @param permissionService the permission service
+ */
+ public void setPermissionService(PermissionService permissionService)
+ {
+ this.m_permissionService = permissionService;
+ }
+
+ /**
+ * @param hiddenAspect the hiddenAspect to set
+ */
+ public void setHiddenAspect(HiddenAspect hiddenAspect)
+ {
+ this.m_hiddenAspect = hiddenAspect;
+ }
+
+ /**
+ * Checks a new path in a move operation to detect whether clients are starting a renaming shuffle - common during
+ * file saving on various clients.
+ *
+ * ALF-3856, ALF-7079, MNT-181
+ */
+ public boolean isRenameShuffle(String newPath)
+ {
+ return m_renameShufflePattern.matcher(newPath.toLowerCase()).matches();
+ }
+
/**
* Split the path into seperate directory path and file name strings.
* If the path is not empty, then there will always be an entry for the filename
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVHelperTest.java b/source/java/org/alfresco/repo/webdav/WebDAVHelperTest.java
index 1d6fe73aa1..4a6ef7df10 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVHelperTest.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVHelperTest.java
@@ -20,13 +20,9 @@ package org.alfresco.repo.webdav;
import static org.junit.Assert.assertEquals;
-import org.alfresco.repo.tenant.TenantService;
-import org.alfresco.service.ServiceRegistry;
-import org.alfresco.service.cmr.security.AuthenticationService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest;
@@ -39,21 +35,18 @@ import org.springframework.mock.web.MockHttpServletRequest;
public class WebDAVHelperTest
{
private WebDAVHelper davHelper;
- private @Mock ServiceRegistry serviceRegistry;
- private @Mock AuthenticationService authService;
- private @Mock TenantService tenantService;
@Before
public void setUp() throws Exception
{
- davHelper = new WebDAVHelper("", serviceRegistry, authService, tenantService);
+ davHelper = new WebDAVHelper();
}
@Test
public void canGetUrlPathPrefixWhenExplicitlySet()
{
// Path prefix explicitly set on helper.
- davHelper = new WebDAVHelper("/my/prefix", serviceRegistry, authService, tenantService);
+ davHelper.setUrlPathPrefix("/my/prefix");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/my/prefix/folder/filename.txt");
String prefix = davHelper.getUrlPathPrefix(request);
assertEquals("/my/prefix/", prefix);
@@ -63,7 +56,7 @@ public class WebDAVHelperTest
public void canGetUrlPathPrefixFromServletPath()
{
// Path prefix not explicitly set on helper.
- davHelper = new WebDAVHelper("", serviceRegistry, authService, tenantService);
+ davHelper.setUrlPathPrefix("");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/before/the-servlet/folder/filename.txt");
// Servlet path will be used to determine path prefix.
request.setServletPath("/the-servlet");
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java
index de926a5bfd..c7ffba6043 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVMethod.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVMethod.java
@@ -46,6 +46,8 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.alfresco.model.ContentModel;
+import org.alfresco.repo.model.filefolder.HiddenAspect;
+import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.AccessDeniedException;
@@ -66,6 +68,8 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.FileFilterMode;
+import org.alfresco.util.FileFilterMode.Client;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -991,6 +995,38 @@ public abstract class WebDAVMethod
return ns.toString();
}
+
+ protected void setHidden(NodeRef nodeRef, boolean isHidden)
+ {
+ int mask = 0;
+ boolean allVisible = true;
+ Visibility webDavVisibility = isHidden ? Visibility.NotVisible : Visibility.Visible;
+ HiddenAspect hiddenAspect = m_davHelper.getHiddenAspect();
+ for (Client client : hiddenAspect.getClients())
+ {
+ Visibility clientVisibility = client == FileFilterMode.getClient() ? webDavVisibility : hiddenAspect
+ .getVisibility(client, nodeRef);
+ if (clientVisibility != Visibility.Visible)
+ {
+ allVisible = false;
+ }
+ mask |= hiddenAspect.getClientVisibilityMask(client, clientVisibility);
+ }
+ if (allVisible)
+ {
+ getNodeService().removeAspect(nodeRef, ContentModel.ASPECT_HIDDEN);
+ }
+ else
+ {
+ hiddenAspect.hideNode(nodeRef, mask);
+ }
+ }
+
+ protected boolean isHidden(NodeRef nodeRef)
+ {
+ return m_davHelper.getHiddenAspect().getVisibility(FileFilterMode.getClient(), nodeRef) != Visibility.Visible;
+ }
+
/**
* Checks if write operation can be performed on node.
*
@@ -1098,16 +1134,18 @@ public abstract class WebDAVMethod
}
else
{
+ // This particular node is not locked so it will not be part of the conditions
+ if (nodeLockToken == null)
+ {
+ return;
+ }
// Node has exclusive lock. Check if conditions contains lock token of the node
// If not throw exception
for (Condition condition : m_conditions)
{
- if (nodeLockToken != null)
+ if (condition.getLockTokensMatch().contains(nodeLockToken))
{
- if (condition.getLockTokensMatch().contains(nodeLockToken))
- {
- return;
- }
+ return;
}
}
throw new WebDAVServerException(WebDAV.WEBDAV_SC_LOCKED);
@@ -1150,9 +1188,9 @@ public abstract class WebDAVMethod
// Checks If header conditions.
// Each condition can contain check of ETag and check of Lock token.
- if (m_conditions == null)
+ if (m_conditions == null || nodeLockToken == null)
{
- // No conditions were provided with "If" request header, so check successful
+ // No conditions were provided with the "If" request header, or this node isn't locked and will not be in the conditions thus the check is successful
return;
}
diff --git a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java
index e85b957a9e..741981ba65 100644
--- a/source/java/org/alfresco/repo/webdav/WebDAVServlet.java
+++ b/source/java/org/alfresco/repo/webdav/WebDAVServlet.java
@@ -41,7 +41,6 @@ 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.SearchService;
-import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.FileFilterMode;
@@ -310,8 +309,6 @@ public class WebDAVServlet extends HttpServlet
transactionService = serviceRegistry.getTransactionService();
tenantService = (TenantService) context.getBean("tenantService");
- AuthenticationService authService = (AuthenticationService) context.getBean("authenticationService");
-
nodeService = (NodeService) context.getBean("NodeService");
searchService = (SearchService) context.getBean("SearchService");
namespaceService = (NamespaceService) context.getBean("NamespaceService");
@@ -321,7 +318,7 @@ public class WebDAVServlet extends HttpServlet
// Collaborator used by WebDAV methods to create activity posts.
activityPoster = new ActivityPosterImpl("WebDAV", activityService);
- // Create the WebDAV helper
+ // Get the WebDAV helper
m_davHelper = (WebDAVHelper) context.getBean("webDAVHelper");
// Initialize the root node