From 2384aba9bc6389880b79eeb633f698376a02dfda Mon Sep 17 00:00:00 2001 From: Kevin Roast Date: Fri, 26 Jan 2007 16:25:03 +0000 Subject: [PATCH] =?UTF-8?q?.=20AVM=20cut/copy=20item=20clipboard=20support?= =?UTF-8?q?=20=20-=20AVM=20files=20and=20folders=20can=20be=20cut=20and=20?= =?UTF-8?q?copied=20to=20the=20clipboard=20=20-=20AVM=20files=20and=20fold?= =?UTF-8?q?ers=20can=20be=20pasted=20(copied=20or=20moved)=20around=20a=20?= =?UTF-8?q?sandbox=20=20-=20The=20usual=20"Copy=20of=20=E2=80=A6"=20filena?= =?UTF-8?q?me=20logic=20applies=20=20-=20Cut=20and=20Copy=20actions=20now?= =?UTF-8?q?=20appear=20for=20AVM=20nodes=20(as=20appropriate=20for=20user?= =?UTF-8?q?=20permissions)=20=20-=20Refactored=20logic=20for=20cut/copy=20?= =?UTF-8?q?into=20specific=20clipboard=20item=20classes=20-=20WorkspaceCli?= =?UTF-8?q?pboardItem=20and=20AVMClipboardItem=20.=20Fixed=20unreported=20?= =?UTF-8?q?issue=20where=20the=20DownloadContentServlet=20was=20unable=20t?= =?UTF-8?q?o=20open=20content=20for=20AVM=20paths=20containing=20a=20space?= =?UTF-8?q?=20character=20.=20Pager=20control=20border=20style=20alignment?= =?UTF-8?q?=20improvement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4943 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../servlet/BaseDownloadContentServlet.java | 3 +- .../web/bean/clipboard/AVMClipboardItem.java | 138 +++++++ .../bean/clipboard/AbstractClipboardItem.java | 71 +++- .../web/bean/clipboard/ClipboardBean.java | 346 +++--------------- .../web/bean/clipboard/ClipboardItem.java | 9 +- .../clipboard/WorkspaceClipboardItem.java | 236 ++++++++++++ .../alfresco/web/bean/wcm/AVMBrowseBean.java | 2 + source/web/WEB-INF/faces-config-beans.xml | 22 +- source/web/css/main.css | 2 +- 9 files changed, 482 insertions(+), 347 deletions(-) diff --git a/source/java/org/alfresco/web/app/servlet/BaseDownloadContentServlet.java b/source/java/org/alfresco/web/app/servlet/BaseDownloadContentServlet.java index 11a965866f..7064861dbd 100644 --- a/source/java/org/alfresco/web/app/servlet/BaseDownloadContentServlet.java +++ b/source/java/org/alfresco/web/app/servlet/BaseDownloadContentServlet.java @@ -19,6 +19,7 @@ package org.alfresco.web.app.servlet; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.SocketException; +import java.net.URLDecoder; import java.net.URLEncoder; import java.text.MessageFormat; import java.util.Date; @@ -138,7 +139,7 @@ public abstract class BaseDownloadContentServlet extends BaseServlet // assume 'workspace' or other NodeRef based protocol for remaining URL elements StoreRef storeRef = new StoreRef(t.nextToken(), t.nextToken()); - String id = t.nextToken(); + String id = URLDecoder.decode(t.nextToken(), "UTF-8"); // build noderef from the appropriate URL elements nodeRef = new NodeRef(storeRef, id); diff --git a/source/java/org/alfresco/web/bean/clipboard/AVMClipboardItem.java b/source/java/org/alfresco/web/bean/clipboard/AVMClipboardItem.java index 198b49e235..aeaa651ff3 100644 --- a/source/java/org/alfresco/web/bean/clipboard/AVMClipboardItem.java +++ b/source/java/org/alfresco/web/bean/clipboard/AVMClipboardItem.java @@ -16,7 +16,19 @@ */ package org.alfresco.web.bean.clipboard; +import javax.faces.context.FacesContext; +import javax.transaction.UserTransaction; + +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.service.cmr.avm.AVMExistsException; +import org.alfresco.service.cmr.avm.AVMService; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.bean.wcm.AVMBrowseBean; /** * Class representing an 'avm' store protocol clipboard item @@ -25,6 +37,8 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public class AVMClipboardItem extends AbstractClipboardItem { + private static final String AVM_PASTE_VIEW_ID = "/jsp/wcm/browse-sandbox.jsp"; + /** * @param ref * @param mode @@ -41,4 +55,128 @@ public class AVMClipboardItem extends AbstractClipboardItem { return false; } + + /** + * @see org.alfresco.web.bean.clipboard.ClipboardItem#canPasteToViewId(java.lang.String) + */ + public boolean canPasteToViewId(String viewId) + { + // TODO: add 'workspace' paste view when interstore copy/move is supported + return (AVM_PASTE_VIEW_ID.equals(viewId)); + } + + /** + * @see org.alfresco.web.bean.clipboard.ClipboardItem#paste(javax.faces.context.FacesContext, java.lang.String, int) + */ + public boolean paste(FacesContext fc, String viewId, int action) throws Throwable + { + if (AVM_PASTE_VIEW_ID.equals(viewId)) + { + AVMBrowseBean avmBrowseBean = (AVMBrowseBean)FacesHelper.getManagedBean(fc, AVMBrowseBean.BEAN_NAME); + + String destPath = avmBrowseBean.getCurrentPath(); + NodeRef destRef = AVMNodeConverter.ToNodeRef(-1, destPath); + String sourcePath = AVMNodeConverter.ToAVMVersionPath(getNodeRef()).getSecond(); + + FileFolderService fileFolderService = getServiceRegistry().getFileFolderService(); + AVMService avmService = getServiceRegistry().getAVMService(); + + // initial name to attempt the copy of the item with + String name = getName(); + + boolean operationComplete = false; + while (operationComplete == false) + { + UserTransaction tx = null; + try + { + // attempt each copy/paste in its own transaction + tx = Repository.getUserTransaction(fc); + tx.begin(); + if (getMode() == ClipboardStatus.COPY) + { + // COPY operation + if (logger.isDebugEnabled()) + logger.debug("Attempting to copy node path: " + sourcePath + " into path: " + destPath); + + // copy the avm path + // first check that we are not attempting to copy a duplicate into the same parent + if (AVMNodeConverter.ExtendAVMPath(destPath, name).equals(sourcePath)) + { + // manually change the name if this occurs + String copyOf = Application.getMessage(fc, MSG_COPY_OF); + name = copyOf + ' ' + name; + } + /*fileFolderService.copy( + getNodeRef(), + destRef, + name);*/ + avmService.copy(-1, sourcePath, destPath, name); + + // if we get here without an exception, the clipboard copy operation was successful + operationComplete = true; + } + else + { + // MOVE operation + if (logger.isDebugEnabled()) + logger.debug("Attempting to move node path: " + sourcePath + " into path: " + destRef); + + // move the avm path + /*fileFolderService.move( + getNodeRef(), + destRef, + name);*/ + avmService.rename(AVMNodeConverter.SplitBase(sourcePath)[0], getName(), + destPath, name); + + // if we get here without an exception, the clipboard move operation was successful + operationComplete = true; + } + } + catch (FileExistsException fileExistsErr) + { + if (getMode() != ClipboardStatus.COPY) + { + // we should not rename an item when it is being moved - so exit + throw fileExistsErr; + } + } + catch (AVMExistsException avmExistsErr) + { + if (getMode() != ClipboardStatus.COPY) + { + // we should not rename an item when it is being moved - so exit + throw avmExistsErr; + } + } + catch (Throwable e) + { + // some other type of exception occured - rollback and exit + throw e; + } + finally + { + // rollback if the operation didn't complete + if (operationComplete == false) + { + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + String copyOf = Application.getMessage(fc, MSG_COPY_OF); + name = copyOf + ' ' + name; + } + else + { + // commit the transaction + tx.commit(); + } + } + } + return operationComplete; + } + else + { + // TODO: support 'workspace' destination view... + return false; + } + } } diff --git a/source/java/org/alfresco/web/bean/clipboard/AbstractClipboardItem.java b/source/java/org/alfresco/web/bean/clipboard/AbstractClipboardItem.java index 61d3607e9d..54e324491b 100644 --- a/source/java/org/alfresco/web/bean/clipboard/AbstractClipboardItem.java +++ b/source/java/org/alfresco/web/bean/clipboard/AbstractClipboardItem.java @@ -16,15 +16,21 @@ */ package org.alfresco.web.bean.clipboard; +import java.util.List; + import javax.faces.context.FacesContext; import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; +import org.alfresco.repo.search.QueryParameterDefImpl; +import org.alfresco.service.ServiceRegistry; +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.QueryParameterDefinition; import org.alfresco.service.namespace.QName; -import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Repository; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Base class representing a single item added to the clipboard. @@ -33,6 +39,24 @@ import org.alfresco.web.bean.repository.Repository; */ abstract class AbstractClipboardItem implements ClipboardItem { + protected static Log logger = LogFactory.getLog(ClipboardBean.class); + + protected static final String MSG_COPY_OF = "copy_of"; + + /** Shallow search for nodes with a name pattern */ + private static final String XPATH_QUERY_NODE_MATCH = "./*[like(@cm:name, $cm:name, false)]"; + + private ServiceRegistry services = null; + + protected NodeRef ref; + protected ClipboardStatus mode; + + // cached values + private String name; + private QName type; + private String icon; + + /** * Constructor * @@ -54,7 +78,8 @@ abstract class AbstractClipboardItem implements ClipboardItem { if (this.name == null) { - this.name = (String)getNodeService().getProperty(this.ref, ContentModel.PROP_NAME); + this.name = (String)getServiceRegistry().getNodeService().getProperty( + this.ref, ContentModel.PROP_NAME); } return this.name; } @@ -63,7 +88,7 @@ abstract class AbstractClipboardItem implements ClipboardItem { if (this.type == null) { - this.type = getNodeService().getType(this.ref); + this.type = getServiceRegistry().getNodeService().getType(this.ref); } return this.type; } @@ -72,7 +97,8 @@ abstract class AbstractClipboardItem implements ClipboardItem { if (this.icon == null) { - this.icon = (String)getNodeService().getProperty(this.ref, ApplicationModel.PROP_ICON); + this.icon = (String)getServiceRegistry().getNodeService().getProperty( + this.ref, ApplicationModel.PROP_ICON); } return this.icon; } @@ -114,16 +140,33 @@ abstract class AbstractClipboardItem implements ClipboardItem return ref.hashCode(); } - protected static NodeService getNodeService() + protected ServiceRegistry getServiceRegistry() { - return Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNodeService(); + if (services == null) + { + services = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); + } + return services; } - protected NodeRef ref; - protected ClipboardStatus mode; - - // cached values - private String name; - private QName type; - private String icon; + protected boolean checkExists(String name, NodeRef parent) + { + QueryParameterDefinition[] params = new QueryParameterDefinition[1]; + params[0] = new QueryParameterDefImpl( + ContentModel.PROP_NAME, + getServiceRegistry().getDictionaryService().getDataType( + DataTypeDefinition.TEXT), + true, + name); + + // execute the query + List nodeRefs = getServiceRegistry().getSearchService().selectNodes( + parent, + XPATH_QUERY_NODE_MATCH, + params, + getServiceRegistry().getNamespaceService(), + false); + + return (nodeRefs.size() != 0); + } } diff --git a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java index d3003d4e68..8c0d32ab5c 100644 --- a/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java +++ b/source/java/org/alfresco/web/bean/clipboard/ClipboardBean.java @@ -16,36 +16,17 @@ */ package org.alfresco.web.bean.clipboard; -import java.io.Serializable; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; -import javax.transaction.UserTransaction; -import org.alfresco.model.ApplicationModel; -import org.alfresco.model.ContentModel; -import org.alfresco.repo.search.QueryParameterDefImpl; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.model.FileExistsException; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.CopyService; 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.QueryParameterDefinition; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.QName; import org.alfresco.web.app.Application; import org.alfresco.web.app.context.UIContextService; -import org.alfresco.web.bean.NavigationBean; -import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.ui.common.Utils; import org.alfresco.web.ui.common.component.UIActionLink; import org.alfresco.web.ui.repo.component.shelf.UIClipboardShelfItem; @@ -53,54 +34,29 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** + * Bean backing the Clipboard shelf functionality. + *

+ * The clipboard bean is responsible for processing Cut/Copy requests to the clipboard + * and for executing the various Paste calls available to the user. + * * @author Kevin Roast */ public class ClipboardBean { + private static Log logger = LogFactory.getLog(ClipboardBean.class); + + /** I18N messages */ + private static final String MSG_ERROR_PASTE = "error_paste"; + + /** Current state of the clipboard items */ + private List items = new ArrayList(4); + + // ------------------------------------------------------------------------------ // Bean property getters and setters - - /** - * @param nodeService The NodeService to set. - */ - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - /** - * @param fileFolderService The FileFolderService to set. - */ - public void setFileFolderService(FileFolderService fileFolderService) - { - this.fileFolderService = fileFolderService; - } /** - * @param copyService The CopyService to set. - */ - public void setCopyService(CopyService copyService) - { - this.copyService = copyService; - } - - /** - * @param searchService The SearchService to set. - */ - public void setSearchService(SearchService searchService) - { - this.searchService = searchService; - } - - /** - * @param navigator The NavigationBean to set. - */ - public void setNavigator(NavigationBean navigator) - { - this.navigator = navigator; - } - - /** - * @return Returns the clipboard items. + * @return Returns a list representing the items on the user clipboard. */ public List getItems() { @@ -108,7 +64,7 @@ public class ClipboardBean } /** - * @param items The clipboard items to set. + * @param items List representing the items on the user clipboard. */ public void setItems(List items) { @@ -185,30 +141,30 @@ public class ClipboardBean { // paste all for (int i=0; i newItems = new ArrayList(this.items.size()); - for (int i=0; i props = new HashMap(2, 1.0f); - props.put(ContentModel.PROP_NAME, name + ".lnk"); - props.put(ContentModel.PROP_LINK_DESTINATION, item.getNodeRef()); - if (dd.isSubClass(item.getType(), ContentModel.TYPE_CONTENT)) - { - // create File Link node - ChildAssociationRef childRef = this.nodeService.createNode( - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - ApplicationModel.TYPE_FILELINK, - props); - - // apply the titled aspect - title and description - Map titledProps = new HashMap(2, 1.0f); - titledProps.put(ContentModel.PROP_TITLE, name); - titledProps.put(ContentModel.PROP_DESCRIPTION, name); - this.nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps); - } - else - { - // create Folder link node - ChildAssociationRef childRef = this.nodeService.createNode( - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - ApplicationModel.TYPE_FOLDERLINK, - props); - - // apply the uifacets aspect - icon, title and description props - Map uiFacetsProps = new HashMap(4, 1.0f); - uiFacetsProps.put(ApplicationModel.PROP_ICON, "space-icon-link"); - uiFacetsProps.put(ContentModel.PROP_TITLE, name); - uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, name); - this.nodeService.addAspect(childRef.getChildRef(), ApplicationModel.ASPECT_UIFACETS, uiFacetsProps); - } - - // if we get here without an exception, the clipboard link operation was successful - operationComplete = true; - } - } - else - { - // COPY operation - if (logger.isDebugEnabled()) - logger.debug("Attempting to copy node ID: " + item.getId() + " into node ID: " + destRef.getId()); - - if (dd.isSubClass(item.getType(), ContentModel.TYPE_CONTENT) || - dd.isSubClass(item.getType(), ContentModel.TYPE_FOLDER)) - { - // copy the file/folder - // first check that we are not attempting to copy a duplicate into the same parent - if (destRef.equals(assocRef.getParentRef()) && name.equals(item.getName())) - { - // manually change the name if this occurs - String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF); - name = copyOf + ' ' + name; - } - this.fileFolderService.copy( - item.getNodeRef(), - destRef, - name); - } - else - { - // copy the node - if (checkExists(name, destRef) == false) - { - this.copyService.copy( - item.getNodeRef(), - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName(), - true); - } - } - - // if we get here without an exception, the clipboard copy operation was successful - operationComplete = true; - } - } - else - { - // MOVE operation - if (logger.isDebugEnabled()) - logger.debug("Attempting to move node ID: " + item.getId() + " into node ID: " + destRef.getId()); - - if (dd.isSubClass(item.getType(), ContentModel.TYPE_CONTENT) || - dd.isSubClass(item.getType(), ContentModel.TYPE_FOLDER)) - { - // move the file/folder - this.fileFolderService.move( - item.getNodeRef(), - destRef, - name); - } - else - { - // move the node - this.nodeService.moveNode( - item.getNodeRef(), - destRef, - ContentModel.ASSOC_CONTAINS, - assocRef.getQName()); - } - - // if we get here without an exception, the clipboard move operation was successful - operationComplete = true; - } - } - catch (FileExistsException fileExistsErr) - { - if (item.getMode() != ClipboardStatus.COPY) - { - // we should not rename an item when it is being moved - so exit - throw fileExistsErr; - } - } - catch (Throwable e) - { - // some other type of exception occured - rollback and exit - throw e; - } - finally - { - // rollback if the operation didn't complete - if (operationComplete == false) - { - try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} - String copyOf = Application.getMessage(FacesContext.getCurrentInstance(), MSG_COPY_OF); - name = copyOf + ' ' + name; - } - else - { - // commit the transaction - tx.commit(); - } - } - } - } - - private boolean checkExists(String name, NodeRef parent) - { - ServiceRegistry services = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); - - QueryParameterDefinition[] params = new QueryParameterDefinition[1]; - params[0] = new QueryParameterDefImpl( - ContentModel.PROP_NAME, - services.getDictionaryService().getDataType( - DataTypeDefinition.TEXT), - true, - name); - - // execute the query - List nodeRefs = searchService.selectNodes( - parent, - XPATH_QUERY_NODE_MATCH, - params, - services.getNamespaceService(), - false); - - return (nodeRefs.size() != 0); + return item.paste(fc, fc.getViewRoot().getViewId(), action); } /** @@ -481,36 +249,4 @@ public class ClipboardBean } } } - - - // ------------------------------------------------------------------------------ - // Private data - - private static Log logger = LogFactory.getLog(ClipboardBean.class); - - /** I18N messages */ - private static final String MSG_ERROR_PASTE = "error_paste"; - private static final String MSG_COPY_OF = "copy_of"; - private static final String MSG_LINK_TO = "link_to"; - - /** Shallow search for nodes with a name pattern */ - private static final String XPATH_QUERY_NODE_MATCH = "./*[like(@cm:name, $cm:name, false)]"; - - /** The NodeService to be used by the bean */ - protected NodeService nodeService; - - /** The FileFolderService to be used by the bean */ - protected FileFolderService fileFolderService; - - /** The CopyService to be used by the bean */ - protected CopyService copyService; - - /** The SearchService to be used by the bean */ - protected SearchService searchService; - - /** The NavigationBean reference */ - protected NavigationBean navigator; - - /** Current state of the clipboard items */ - private List items = new ArrayList(4); } diff --git a/source/java/org/alfresco/web/bean/clipboard/ClipboardItem.java b/source/java/org/alfresco/web/bean/clipboard/ClipboardItem.java index 2b8fe47785..7e7d80d428 100644 --- a/source/java/org/alfresco/web/bean/clipboard/ClipboardItem.java +++ b/source/java/org/alfresco/web/bean/clipboard/ClipboardItem.java @@ -18,13 +18,8 @@ package org.alfresco.web.bean.clipboard; import javax.faces.context.FacesContext; -import org.alfresco.model.ApplicationModel; -import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; -import org.alfresco.web.bean.repository.Node; -import org.alfresco.web.bean.repository.Repository; /** * Simple class representing a single item added to the clipboard. @@ -46,4 +41,8 @@ public interface ClipboardItem public NodeRef getNodeRef(); public boolean supportsLink(); + + public boolean canPasteToViewId(String viewId); + + public boolean paste(FacesContext fc, String viewId, int action) throws Throwable; } diff --git a/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java b/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java index b39158b93e..292013c56c 100644 --- a/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java +++ b/source/java/org/alfresco/web/bean/clipboard/WorkspaceClipboardItem.java @@ -16,7 +16,28 @@ */ package org.alfresco.web.bean.clipboard; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.faces.context.FacesContext; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ApplicationModel; +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.NavigationBean; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.repo.component.shelf.UIClipboardShelfItem; /** * Class representing a 'workspace' store protocol clipboard item @@ -25,6 +46,11 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public class WorkspaceClipboardItem extends AbstractClipboardItem { + private static final String WORKSPACE_PASTE_VIEW_ID = "/jsp/browse/browse.jsp"; + + private static final String MSG_LINK_TO = "link_to"; + + /** * @param ref * @param mode @@ -41,4 +67,214 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem { return true; } + + /** + * @see org.alfresco.web.bean.clipboard.ClipboardItem#canPasteToViewId(java.lang.String) + */ + public boolean canPasteToViewId(String viewId) + { + // TODO: add 'avm' paste view when interstore copy/move is supported + return (WORKSPACE_PASTE_VIEW_ID.equals(viewId)); + } + + /** + * @see org.alfresco.web.bean.clipboard.ClipboardItem#paste(javax.faces.context.FacesContext, java.lang.String, int) + */ + public boolean paste(FacesContext fc, String viewId, int action) + throws Throwable + { + if (WORKSPACE_PASTE_VIEW_ID.equals(viewId)) + { + NavigationBean navigator = (NavigationBean)FacesHelper.getManagedBean(fc, NavigationBean.BEAN_NAME); + NodeRef destRef = new NodeRef(Repository.getStoreRef(), navigator.getCurrentNodeId()); + + DictionaryService dd = getServiceRegistry().getDictionaryService(); + NodeService nodeService = getServiceRegistry().getNodeService(); + FileFolderService fileFolderService = getServiceRegistry().getFileFolderService(); + CopyService copyService = getServiceRegistry().getCopyService(); + + // TODO: Should we be using primary parent here? + // We are assuming that the item exists in only a single parent and that the source for + // the clipboard operation (e.g. the source folder) is specifically that parent node. + // So does not allow for more than one possible parent node - or for linked objects! + // This code should be refactored to use a parent ID when appropriate. + ChildAssociationRef assocRef = nodeService.getPrimaryParent(getNodeRef()); + + // initial name to attempt the copy of the item with + String name = getName(); + if (action == UIClipboardShelfItem.ACTION_PASTE_LINK) + { + // copy as link was specifically requested by the user + String linkTo = Application.getMessage(fc, MSG_LINK_TO); + name = linkTo + ' ' + name; + } + + boolean operationComplete = false; + while (operationComplete == false) + { + UserTransaction tx = null; + try + { + // attempt each copy/paste in its own transaction + tx = Repository.getUserTransaction(fc); + tx.begin(); + if (getMode() == ClipboardStatus.COPY) + { + if (action == UIClipboardShelfItem.ACTION_PASTE_LINK) + { + // LINK operation + if (logger.isDebugEnabled()) + logger.debug("Attempting to link node ID: " + getId() + " into node ID: " + destRef.getId()); + + // we create a special Link Object node that has a property to reference the original + // create the node using the nodeService (can only use FileFolderService for content) + if (checkExists(name + ".lnk", destRef) == false) + { + Map props = new HashMap(2, 1.0f); + props.put(ContentModel.PROP_NAME, name + ".lnk"); + props.put(ContentModel.PROP_LINK_DESTINATION, getNodeRef()); + if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT)) + { + // create File Link node + ChildAssociationRef childRef = nodeService.createNode( + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + ApplicationModel.TYPE_FILELINK, + props); + + // apply the titled aspect - title and description + Map titledProps = new HashMap(2, 1.0f); + titledProps.put(ContentModel.PROP_TITLE, name); + titledProps.put(ContentModel.PROP_DESCRIPTION, name); + nodeService.addAspect(childRef.getChildRef(), ContentModel.ASPECT_TITLED, titledProps); + } + else + { + // create Folder link node + ChildAssociationRef childRef = nodeService.createNode( + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + ApplicationModel.TYPE_FOLDERLINK, + props); + + // apply the uifacets aspect - icon, title and description props + Map uiFacetsProps = new HashMap(4, 1.0f); + uiFacetsProps.put(ApplicationModel.PROP_ICON, "space-icon-link"); + uiFacetsProps.put(ContentModel.PROP_TITLE, name); + uiFacetsProps.put(ContentModel.PROP_DESCRIPTION, name); + nodeService.addAspect(childRef.getChildRef(), ApplicationModel.ASPECT_UIFACETS, uiFacetsProps); + } + + // if we get here without an exception, the clipboard link operation was successful + operationComplete = true; + } + } + else + { + // COPY operation + if (logger.isDebugEnabled()) + logger.debug("Attempting to copy node ID: " + getId() + " into node ID: " + destRef.getId()); + + if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) || + dd.isSubClass(getType(), ContentModel.TYPE_FOLDER)) + { + // copy the file/folder + // first check that we are not attempting to copy a duplicate into the same parent + if (destRef.equals(assocRef.getParentRef()) && name.equals(getName())) + { + // manually change the name if this occurs + String copyOf = Application.getMessage(fc, MSG_COPY_OF); + name = copyOf + ' ' + name; + } + fileFolderService.copy( + getNodeRef(), + destRef, + name); + } + else + { + // copy the node + if (checkExists(name, destRef) == false) + { + copyService.copy( + getNodeRef(), + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName(), + true); + } + } + + // if we get here without an exception, the clipboard copy operation was successful + operationComplete = true; + } + } + else + { + // MOVE operation + if (logger.isDebugEnabled()) + logger.debug("Attempting to move node ID: " + getId() + " into node ID: " + destRef.getId()); + + if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) || + dd.isSubClass(getType(), ContentModel.TYPE_FOLDER)) + { + // move the file/folder + fileFolderService.move( + getNodeRef(), + destRef, + name); + } + else + { + // move the node + nodeService.moveNode( + getNodeRef(), + destRef, + ContentModel.ASSOC_CONTAINS, + assocRef.getQName()); + } + + // if we get here without an exception, the clipboard move operation was successful + operationComplete = true; + } + } + catch (FileExistsException fileExistsErr) + { + if (getMode() != ClipboardStatus.COPY) + { + // we should not rename an item when it is being moved - so exit + throw fileExistsErr; + } + } + catch (Throwable e) + { + // some other type of exception occured - rollback and exit + throw e; + } + finally + { + // rollback if the operation didn't complete + if (operationComplete == false) + { + try { if (tx != null) {tx.rollback();} } catch (Exception tex) {} + String copyOf = Application.getMessage(fc, MSG_COPY_OF); + name = copyOf + ' ' + name; + } + else + { + // commit the transaction + tx.commit(); + } + } + } + return operationComplete; + } + else + { + // TODO: support 'avm' destination view... + return false; + } + } } diff --git a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java index b500ef7aa5..e8374c4ab2 100644 --- a/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java +++ b/source/java/org/alfresco/web/bean/wcm/AVMBrowseBean.java @@ -77,6 +77,8 @@ import org.apache.commons.logging.LogFactory; */ public class AVMBrowseBean implements IContextListener { + public static final String BEAN_NAME = "AVMBrowseBean"; + private static final Log LOGGER = LogFactory.getLog(AVMBrowseBean.class); private static final String MSG_REVERT_SUCCESS = "revert_success"; diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 17549ae9c2..f612dcc8ff 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -237,31 +237,11 @@ - The bean that holds a users Clipboard state. + The bean that manages a users Clipboard state. ClipboardBean org.alfresco.web.bean.clipboard.ClipboardBean session - - navigator - #{NavigationBean} - - - nodeService - #{NodeService} - - - fileFolderService - #{FileFolderService} - - - copyService - #{CopyService} - - - searchService - #{SearchService} - diff --git a/source/web/css/main.css b/source/web/css/main.css index 05ce5f20c4..842e4f4bc9 100644 --- a/source/web/css/main.css +++ b/source/web/css/main.css @@ -435,7 +435,7 @@ a.topToolbarLinkHighlight, a.topToolbarLinkHighlight:link, a.topToolbarLinkHighl .pager { - padding: 6px 4px 3px 4px; + padding: 3px; border: 1px dotted #cccccc; }