View Javadoc
1   /***************************************************************************
2    * Copyright (C) 2003-2009 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   *
17   **************************************************************************/
18  package org.exoplatform.ecm.webui.component.explorer.rightclick.manager;
19  
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.regex.Matcher;
26  
27  import javax.jcr.AccessDeniedException;
28  import javax.jcr.Node;
29  import javax.jcr.NodeIterator;
30  import javax.jcr.PathNotFoundException;
31  import javax.jcr.Session;
32  import javax.jcr.Workspace;
33  import javax.jcr.lock.LockException;
34  import javax.jcr.nodetype.ConstraintViolationException;
35  import javax.portlet.PortletPreferences;
36  
37  import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer;
38  import org.exoplatform.ecm.webui.component.explorer.UIWorkingArea;
39  import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotTrashHomeNodeFilter;
40  import org.exoplatform.ecm.webui.component.explorer.control.listener.UIWorkingAreaActionListener;
41  import org.exoplatform.ecm.webui.utils.JCRExceptionManager;
42  import org.exoplatform.ecm.utils.lock.LockUtil;
43  import org.exoplatform.ecm.webui.utils.PermissionUtil;
44  import org.exoplatform.ecm.webui.utils.Utils;
45  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
46  import org.exoplatform.services.cms.thumbnail.ThumbnailService;
47  import org.exoplatform.services.listener.ListenerService;
48  import org.exoplatform.services.log.ExoLogger;
49  import org.exoplatform.services.log.Log;
50  import org.exoplatform.services.wcm.core.NodetypeConstant;
51  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
52  import org.exoplatform.web.application.ApplicationMessage;
53  import org.exoplatform.webui.application.WebuiRequestContext;
54  import org.exoplatform.webui.application.portlet.PortletRequestContext;
55  import org.exoplatform.webui.config.annotation.ComponentConfig;
56  import org.exoplatform.webui.config.annotation.EventConfig;
57  import org.exoplatform.webui.core.UIApplication;
58  import org.exoplatform.webui.core.UIComponent;
59  import org.exoplatform.webui.event.Event;
60  import org.exoplatform.webui.ext.filter.UIExtensionFilter;
61  import org.exoplatform.webui.ext.filter.UIExtensionFilters;
62  import org.exoplatform.webui.ext.manager.UIAbstractManager;
63  import org.exoplatform.webui.ext.manager.UIAbstractManagerComponent;
64  
65  /**
66   * Created by The eXo Platform SARL
67   * Author : Hoang Van Hung
68   *          hunghvit@gmail.com
69   * Aug 6, 2009
70   */
71  
72  @ComponentConfig(
73      events = {
74        @EventConfig(listeners = MoveNodeManageComponent.MoveNodeActionListener.class, confirm="UIWorkingArea.msg.confirm-move")
75      }
76  )
77  
78  public class MoveNodeManageComponent extends UIAbstractManagerComponent {
79  
80    private static final List<UIExtensionFilter> FILTERS
81        = Arrays.asList(new UIExtensionFilter[]{ new IsNotTrashHomeNodeFilter()} );
82  
83    @UIExtensionFilters
84    public List<UIExtensionFilter> getFilters() {
85      return FILTERS;
86    }
87  
88    private static final Log LOG = ExoLogger.getLogger(MoveNodeManageComponent.class.getName());
89  
90    private static void processMultipleSelection(String nodePath, String destPath,
91        Event<? extends UIComponent> event) throws Exception {
92      UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
93      Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(destPath);
94      String wsName = null;
95      if (matcher.find()) {
96        wsName = matcher.group(1);
97        destPath = matcher.group(2);
98      } else {
99        throw new IllegalArgumentException("The ObjectId is invalid '" + destPath + "'");
100     }
101     Session session = uiExplorer.getSessionByWorkspace(wsName);
102     UIApplication uiApp = event.getSource().getAncestorOfType(UIApplication.class);
103     if (destPath.startsWith(nodePath)) {
104       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.bound-move-exception", null,
105           ApplicationMessage.WARNING));
106 
107       return;
108     }
109     Node destNode;
110     try {
111       // Use the method getNodeByPath because it is link aware
112       destNode = uiExplorer.getNodeByPath(destPath, session);
113       // Reset the path to manage the links that potentially create virtual path
114       destPath = destNode.getPath();
115       // Reset the session to manage the links that potentially change of
116       // workspace
117       session = destNode.getSession();
118     } catch (PathNotFoundException path) {
119       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.path-not-found-exception", null,
120           ApplicationMessage.WARNING));
121 
122       return;
123     } catch (Exception e) {
124       JCRExceptionManager.process(uiApp, e);
125       return;
126     }
127     if (!PermissionUtil.canAddNode(destNode)) {
128       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.can-not-move-node", null,
129           ApplicationMessage.WARNING));
130 
131       uiExplorer.updateAjax(event);
132       return;
133     }
134     if (uiExplorer.nodeIsLocked(destNode)) {
135       Object[] arg = { destPath };
136       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.node-locked", arg,
137           ApplicationMessage.WARNING));
138 
139       return;
140     }
141     if (!destNode.isCheckedOut()) {
142       uiApp.addMessage(new ApplicationMessage("UIActionBar.msg.node-checkedin", null));
143 
144       return;
145     }
146     try {
147       if (nodePath.indexOf(";") > -1) {
148           moveMultiNode(nodePath.split(";"), destNode, event);
149       } else {
150           moveNode(nodePath, null, destNode, event);
151       }
152       session.save();
153       uiExplorer.updateAjax(event);
154     } catch (AccessDeniedException ace) {
155       Object[] arg = { destPath };
156       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.has-not-add-permission", arg,
157           ApplicationMessage.WARNING));
158 
159       return;
160     } catch (LockException lock) {
161       Object[] arg = { nodePath };
162       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.node-locked", arg,
163           ApplicationMessage.WARNING));
164 
165       return;
166     } catch (ConstraintViolationException constraint) {
167       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.move-constraint-exception", null,
168           ApplicationMessage.WARNING));
169 
170       return;
171     } catch (Exception e) {
172       if (LOG.isErrorEnabled()) {
173         LOG.error("an unexpected error occurs while selecting the node", e);
174       }
175       JCRExceptionManager.process(uiApp, e);
176       return;
177     }
178   }
179 
180   private static Node getNodeByPath(String srcPath, UIJCRExplorer uiExplorer) throws Exception {
181     Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(srcPath);
182     String wsName = null;
183     if (matcher.find()) {
184       wsName = matcher.group(1);
185       srcPath = matcher.group(2);
186     } else {
187       throw new IllegalArgumentException("The ObjectId is invalid '" + srcPath + "'");
188     }
189     Session srcSession = uiExplorer.getSessionByWorkspace(wsName);
190     return uiExplorer.getNodeByPath(srcPath, srcSession, false);
191   }
192 
193   private static void moveNode(String srcPath, Node selectedNode, Node destNode, Event<?> event) throws Exception {
194     UIComponent uiComponent = (UIComponent)event.getSource();
195     UIJCRExplorer uiExplorer = uiComponent.getAncestorOfType(UIJCRExplorer.class);
196     UIApplication uiApp = uiComponent.getAncestorOfType(UIApplication.class);
197     if (srcPath.indexOf(":/") > -1 || (selectedNode != null)) {
198       String[] arrSrcPath = srcPath.split(":/");
199       if ((srcPath.contains(":/") && ("/" + arrSrcPath[1]).equals(destNode.getPath()))
200           || (selectedNode != null && selectedNode.equals(destNode))) {
201         uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.can-not-move-to-itself",
202                                                 null,
203                                                 ApplicationMessage.WARNING));
204 
205         return;
206       }
207     }
208     if(uiExplorer.isEditingDocument()) {
209       uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.is-editing", null,
210           ApplicationMessage.WARNING));
211       return;
212     }
213     String messagePath = "";
214     try {
215       if (selectedNode == null) {
216         selectedNode = getNodeByPath(srcPath, uiExplorer);
217       }
218       Session srcSession = selectedNode.getSession();
219       if (!selectedNode.isCheckedOut()) {
220         uiApp.addMessage(new ApplicationMessage("UIActionBar.msg.node-checkedin", null,
221             ApplicationMessage.WARNING));
222 
223         return;
224       }
225       uiExplorer.addLockToken(selectedNode);
226       String destPath = destNode.getPath();
227       messagePath = destPath;
228       // Make destination path without index on final name
229       destPath = destPath.concat("/").concat(selectedNode.getName());
230       Workspace srcWorkspace = srcSession.getWorkspace();
231       Workspace destWorkspace = destNode.getSession().getWorkspace();
232       if (srcPath.indexOf(":/") > -1)
233         srcPath = srcPath.substring(srcPath.indexOf(":/") + 1);
234 
235       // Get thumbnail node if source node
236       ThumbnailService thumbnailService = WCMCoreUtils.getService(ThumbnailService.class);
237       Node srcThumbnailNode = thumbnailService.getThumbnailNode(selectedNode);
238 
239       ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class);
240       ActivityCommonService activityService = WCMCoreUtils.getService(ActivityCommonService.class);
241       Node desNode = null;
242       if (srcWorkspace.equals(destWorkspace)) {
243         srcWorkspace.move(srcPath, destPath);
244         //delete EXO_RESTORE_LOCATION if source is in trash
245         removeMixinEXO_RESTORE_LOCATION(srcSession, destPath);
246         desNode = (Node)srcSession.getItem(destPath);
247         LockUtil.changeLockToken(srcPath, desNode);
248         if (activityService.isAcceptedNode(desNode) || desNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
249           listenerService.broadcast(ActivityCommonService.NODE_MOVED_ACTIVITY, desNode, desNode.getPath());
250         }
251         srcSession.save();
252       } else {
253         destWorkspace.clone(srcWorkspace.getName(), srcPath, destPath, false);
254         desNode =(Node) destWorkspace.getSession().getItem(destPath);
255         if (activityService.isAcceptedNode(desNode) || desNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
256           listenerService.broadcast(ActivityCommonService.NODE_MOVED_ACTIVITY, desNode, destPath);
257         }
258         //delete EXO_RESTORE_LOCATION if source is in trash
259         removeMixinEXO_RESTORE_LOCATION(destWorkspace.getSession(), destPath);
260         destWorkspace.getSession().save();
261       }
262 
263       // Update thumbnail node for destination node
264       thumbnailService.copyThumbnailNode(srcThumbnailNode, desNode);
265 
266     } catch (Exception e) {
267       Object[] args = { srcPath, messagePath };
268       uiApp.addMessage(new ApplicationMessage("UIWorkingArea.msg.move-problem", args,
269           ApplicationMessage.WARNING));
270 
271       return;
272     }
273   }
274 
275   private static void removeMixinEXO_RESTORE_LOCATION(Session session, String restorePath) throws Exception {
276     Node sameNameNode = ((Node) session.getItem(restorePath));
277     Node parent = sameNameNode.getParent();
278     String name = sameNameNode.getName();
279     NodeIterator nodeIter = parent.getNodes(name);
280     while (nodeIter.hasNext()) {
281       Node node = nodeIter.nextNode();
282       if (node.isNodeType(Utils.EXO_RESTORELOCATION))
283         node.removeMixin(Utils.EXO_RESTORELOCATION);
284     }
285   }
286 
287   private static void moveMultiNode(String[] srcPaths, Node destNode, Event<? extends UIComponent> event) throws Exception {
288     Map<String, Node> mapNode = new HashMap <String, Node>();
289     UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
290     Node node;
291     for (int i = 0; i < srcPaths.length; i++) {
292       node = getNodeByPath(srcPaths[i], uiExplorer);
293       mapNode.put(node.getPath(), node);
294     }
295     String path = null;
296     Iterator<String> iterator = mapNode.keySet().iterator();
297     while (iterator.hasNext()) {
298       path = iterator.next();
299       node = mapNode.get(path);
300       node.refresh(true);
301       moveNode(node.getPath(), node, destNode, event);
302     }
303   }
304 
305   public static class MoveNodeActionListener extends UIWorkingAreaActionListener<MoveNodeManageComponent> {
306     public void processEvent(Event<MoveNodeManageComponent> event) throws Exception {
307       String nodePath = event.getRequestContext().getRequestParameter(OBJECTID);
308       String destPath = event.getRequestContext().getRequestParameter("destInfo");
309 
310       if (isInTrash(nodePath) && isInTrash(destPath))
311         return;
312       else if (isInTrash(destPath))
313         ((UIWorkingArea)event.getSource().getParent()).getChild(DeleteManageComponent.class).doDelete(nodePath, event);
314       else
315         MoveNodeManageComponent.processMultipleSelection(nodePath.trim(), destPath.trim(), event);
316     }
317 
318     private static boolean isInTrash(String path) {
319       PortletRequestContext pcontext = (PortletRequestContext)WebuiRequestContext.getCurrentInstance();
320       PortletPreferences portletPref = pcontext.getRequest().getPreferences();
321       String trashHomeNodePath = portletPref.getValue(Utils.TRASH_HOME_NODE_PATH, "");
322       String trashWorkspace = portletPref.getValue(Utils.TRASH_WORKSPACE, "");
323 
324       return (path.startsWith(trashHomeNodePath) ||
325           path.startsWith(trashWorkspace + ":" + trashHomeNodePath));
326     }
327   }
328   @Override
329   public Class<? extends UIAbstractManager> getUIAbstractManagerClass() {
330     return null;
331   }
332 
333 }