/*************************************************************************** * Copyright (C) 2003-2009 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. * **************************************************************************/ package org.exoplatform.ecm.webui.component.explorer.rightclick.manager; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import javax.jcr.AccessDeniedException; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.Session; import javax.jcr.Workspace; import javax.jcr.lock.LockException; import javax.jcr.nodetype.ConstraintViolationException; import javax.portlet.PortletPreferences; import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer; import org.exoplatform.ecm.webui.component.explorer.UIWorkingArea; import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotTrashHomeNodeFilter; import org.exoplatform.ecm.webui.component.explorer.control.listener.UIWorkingAreaActionListener; import org.exoplatform.ecm.webui.utils.JCRExceptionManager; import org.exoplatform.ecm.utils.lock.LockUtil; import org.exoplatform.ecm.webui.utils.PermissionUtil; import org.exoplatform.ecm.webui.utils.Utils; import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService; import org.exoplatform.services.cms.thumbnail.ThumbnailService; import org.exoplatform.services.listener.ListenerService; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.wcm.core.NodetypeConstant; import org.exoplatform.services.wcm.utils.WCMCoreUtils; import org.exoplatform.web.application.ApplicationMessage; import org.exoplatform.webui.application.WebuiRequestContext; import org.exoplatform.webui.application.portlet.PortletRequestContext; import org.exoplatform.webui.config.annotation.ComponentConfig; import org.exoplatform.webui.config.annotation.EventConfig; import org.exoplatform.webui.core.UIApplication; import org.exoplatform.webui.core.UIComponent; import org.exoplatform.webui.event.Event; import org.exoplatform.webui.ext.filter.UIExtensionFilter; import org.exoplatform.webui.ext.filter.UIExtensionFilters; import org.exoplatform.webui.ext.manager.UIAbstractManager; import org.exoplatform.webui.ext.manager.UIAbstractManagerComponent; /** * Created by The eXo Platform SARL * Author : Hoang Van Hung * hunghvit@gmail.com * Aug 6, 2009 */ @ComponentConfig( events = { @EventConfig(listeners = MoveNodeManageComponent.MoveNodeActionListener.class, confirm="UIWorkingArea.msg.confirm-move") } ) public class MoveNodeManageComponent extends UIAbstractManagerComponent { private static final List<UIExtensionFilter> FILTERS = Arrays.asList(new UIExtensionFilter[]{ new IsNotTrashHomeNodeFilter()} ); @UIExtensionFilters public List<UIExtensionFilter> getFilters() { return FILTERS; } private static final Log LOG = ExoLogger.getLogger(MoveNodeManageComponent.class.getName()); private static void processMultipleSelection(String nodePath, String destPath, Event<? extends UIComponent> event) throws Exception { UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class); Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(destPath); String wsName = null; if (matcher.find()) { wsName = matcher.group(1); destPath = matcher.group(2); } else { throw new IllegalArgumentException("The ObjectId is invalid '" + destPath + "'"); } Session session = uiExplorer.getSessionByWorkspace(wsName); UIApplication uiApp = event.getSource().getAncestorOfType(UIApplication.class); if (destPath.startsWith(nodePath)) { uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.bound-move-exception", null, ApplicationMessage.WARNING)); return; } Node destNode; try { // Use the method getNodeByPath because it is link aware destNode = uiExplorer.getNodeByPath(destPath, session); // Reset the path to manage the links that potentially create virtual path destPath = destNode.getPath(); // Reset the session to manage the links that potentially change of // workspace session = destNode.getSession(); } catch (PathNotFoundException path) { uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.path-not-found-exception", null, ApplicationMessage.WARNING)); return; } catch (Exception e) { JCRExceptionManager.process(uiApp, e); return; } if (!PermissionUtil.canAddNode(destNode)) { uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.can-not-move-node", null, ApplicationMessage.WARNING)); uiExplorer.updateAjax(event); return; } if (uiExplorer.nodeIsLocked(destNode)) { Object[] arg = { destPath }; uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.node-locked", arg, ApplicationMessage.WARNING)); return; } if (!destNode.isCheckedOut()) { uiApp.addMessage(new ApplicationMessage("UIActionBar.msg.node-checkedin", null)); return; } try { if (nodePath.indexOf(";") > -1) { moveMultiNode(nodePath.split(";"), destNode, event); } else { moveNode(nodePath, null, destNode, event); } session.save(); uiExplorer.updateAjax(event); } catch (AccessDeniedException ace) { Object[] arg = { destPath }; uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.has-not-add-permission", arg, ApplicationMessage.WARNING)); return; } catch (LockException lock) { Object[] arg = { nodePath }; uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.node-locked", arg, ApplicationMessage.WARNING)); return; } catch (ConstraintViolationException constraint) { uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.move-constraint-exception", null, ApplicationMessage.WARNING)); return; } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("an unexpected error occurs while selecting the node", e); } JCRExceptionManager.process(uiApp, e); return; } } private static Node getNodeByPath(String srcPath, UIJCRExplorer uiExplorer) throws Exception { Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(srcPath); String wsName = null; if (matcher.find()) { wsName = matcher.group(1); srcPath = matcher.group(2); } else { throw new IllegalArgumentException("The ObjectId is invalid '" + srcPath + "'"); } Session srcSession = uiExplorer.getSessionByWorkspace(wsName); return uiExplorer.getNodeByPath(srcPath, srcSession, false); } private static void moveNode(String srcPath, Node selectedNode, Node destNode, Event<?> event) throws Exception { UIComponent uiComponent = (UIComponent)event.getSource(); UIJCRExplorer uiExplorer = uiComponent.getAncestorOfType(UIJCRExplorer.class); UIApplication uiApp = uiComponent.getAncestorOfType(UIApplication.class); if (srcPath.indexOf(":/") > -1 || (selectedNode != null)) { String[] arrSrcPath = srcPath.split(":/"); if ((srcPath.contains(":/") && ("/" + arrSrcPath[1]).equals(destNode.getPath())) || (selectedNode != null && selectedNode.equals(destNode))) { uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.can-not-move-to-itself", null, ApplicationMessage.WARNING)); return; } } if(uiExplorer.isEditingDocument()) { uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.is-editing", null, ApplicationMessage.WARNING)); return; } String messagePath = ""; try { if (selectedNode == null) { selectedNode = getNodeByPath(srcPath, uiExplorer); } Session srcSession = selectedNode.getSession(); if (!selectedNode.isCheckedOut()) { uiApp.addMessage(new ApplicationMessage("UIActionBar.msg.node-checkedin", null, ApplicationMessage.WARNING)); return; } uiExplorer.addLockToken(selectedNode); String destPath = destNode.getPath(); messagePath = destPath; // Make destination path without index on final name destPath = destPath.concat("/").concat(selectedNode.getName()); Workspace srcWorkspace = srcSession.getWorkspace(); Workspace destWorkspace = destNode.getSession().getWorkspace(); if (srcPath.indexOf(":/") > -1) srcPath = srcPath.substring(srcPath.indexOf(":/") + 1); // Get thumbnail node if source node ThumbnailService thumbnailService = WCMCoreUtils.getService(ThumbnailService.class); Node srcThumbnailNode = thumbnailService.getThumbnailNode(selectedNode); ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class); ActivityCommonService activityService = WCMCoreUtils.getService(ActivityCommonService.class); Node desNode = null; if (srcWorkspace.equals(destWorkspace)) { srcWorkspace.move(srcPath, destPath); //delete EXO_RESTORE_LOCATION if source is in trash removeMixinEXO_RESTORE_LOCATION(srcSession, destPath); desNode = (Node)srcSession.getItem(destPath); LockUtil.changeLockToken(srcPath, desNode); if (activityService.isAcceptedNode(desNode) || desNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) { listenerService.broadcast(ActivityCommonService.NODE_MOVED_ACTIVITY, desNode, desNode.getPath()); } srcSession.save(); } else { destWorkspace.clone(srcWorkspace.getName(), srcPath, destPath, false); desNode =(Node) destWorkspace.getSession().getItem(destPath); if (activityService.isAcceptedNode(desNode) || desNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) { listenerService.broadcast(ActivityCommonService.NODE_MOVED_ACTIVITY, desNode, destPath); } //delete EXO_RESTORE_LOCATION if source is in trash removeMixinEXO_RESTORE_LOCATION(destWorkspace.getSession(), destPath); destWorkspace.getSession().save(); } // Update thumbnail node for destination node thumbnailService.copyThumbnailNode(srcThumbnailNode, desNode); } catch (Exception e) { Object[] args = { srcPath, messagePath }; uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.move-problem", args, ApplicationMessage.WARNING)); return; } } private static void removeMixinEXO_RESTORE_LOCATION(Session session, String restorePath) throws Exception { Node sameNameNode = ((Node) session.getItem(restorePath)); Node parent = sameNameNode.getParent(); String name = sameNameNode.getName(); NodeIterator nodeIter = parent.getNodes(name); while (nodeIter.hasNext()) { Node node = nodeIter.nextNode(); if (node.isNodeType(Utils.EXO_RESTORELOCATION)) node.removeMixin(Utils.EXO_RESTORELOCATION); } } private static void moveMultiNode(String[] srcPaths, Node destNode, Event<? extends UIComponent> event) throws Exception { Map<String, Node> mapNode = new HashMap <String, Node>(); UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class); Node node; for (int i = 0; i < srcPaths.length; i++) { node = getNodeByPath(srcPaths[i], uiExplorer); mapNode.put(node.getPath(), node); } String path = null; Iterator<String> iterator = mapNode.keySet().iterator(); while (iterator.hasNext()) { path = iterator.next(); node = mapNode.get(path); node.refresh(true); moveNode(node.getPath(), node, destNode, event); } } public static class MoveNodeActionListener extends UIWorkingAreaActionListener<MoveNodeManageComponent> { public void processEvent(Event<MoveNodeManageComponent> event) throws Exception { String nodePath = event.getRequestContext().getRequestParameter(OBJECTID); String destPath = event.getRequestContext().getRequestParameter("destInfo"); if (isInTrash(nodePath) && isInTrash(destPath)) return; else if (isInTrash(destPath)) ((UIWorkingArea)event.getSource().getParent()).getChild(DeleteManageComponent.class).doDelete(nodePath, event); else MoveNodeManageComponent.processMultipleSelection(nodePath.trim(), destPath.trim(), event); } private static boolean isInTrash(String path) { PortletRequestContext pcontext = (PortletRequestContext)WebuiRequestContext.getCurrentInstance(); PortletPreferences portletPref = pcontext.getRequest().getPreferences(); String trashHomeNodePath = portletPref.getValue(Utils.TRASH_HOME_NODE_PATH, ""); String trashWorkspace = portletPref.getValue(Utils.TRASH_WORKSPACE, ""); return (path.startsWith(trashHomeNodePath) || path.startsWith(trashWorkspace + ":" + trashHomeNodePath)); } } @Override public Class<? extends UIAbstractManager> getUIAbstractManagerClass() { return null; } }