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.ArrayList;
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Queue;
26  import java.util.ResourceBundle;
27  import java.util.regex.Matcher;
28  
29  import javax.jcr.AccessDeniedException;
30  import javax.jcr.Node;
31  import javax.jcr.NodeIterator;
32  import javax.jcr.PathNotFoundException;
33  import javax.jcr.PropertyIterator;
34  import javax.jcr.ReferentialIntegrityException;
35  import javax.jcr.Session;
36  import javax.jcr.lock.LockException;
37  import javax.jcr.nodetype.ConstraintViolationException;
38  import javax.jcr.version.VersionException;
39  import javax.portlet.PortletPreferences;
40  
41  import org.apache.commons.lang.StringEscapeUtils;
42  import org.apache.commons.lang.Validate;
43  import org.exoplatform.ecm.webui.component.explorer.UIConfirmMessage;
44  import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer;
45  import org.exoplatform.ecm.webui.component.explorer.UIWorkingArea;
46  import org.exoplatform.ecm.webui.component.explorer.control.filter.CanDeleteNodeFilter;
47  import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotEditingDocumentFilter;
48  import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotLockedFilter;
49  import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotMandatoryChildNode;
50  import org.exoplatform.ecm.webui.component.explorer.control.filter.IsNotTrashHomeNodeFilter;
51  import org.exoplatform.ecm.webui.component.explorer.control.listener.UIWorkingAreaActionListener;
52  import org.exoplatform.ecm.webui.utils.JCRExceptionManager;
53  import org.exoplatform.ecm.utils.lock.LockUtil;
54  import org.exoplatform.ecm.webui.utils.PermissionUtil;
55  import org.exoplatform.ecm.webui.utils.Utils;
56  import org.exoplatform.services.cms.actions.ActionServiceContainer;
57  import org.exoplatform.services.cms.documents.TrashService;
58  import org.exoplatform.services.cms.folksonomy.NewFolksonomyService;
59  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
60  import org.exoplatform.services.cms.link.LinkManager;
61  import org.exoplatform.services.cms.link.LinkUtils;
62  import org.exoplatform.services.cms.relations.RelationsService;
63  import org.exoplatform.services.cms.taxonomy.TaxonomyService;
64  import org.exoplatform.services.cms.templates.TemplateService;
65  import org.exoplatform.services.cms.thumbnail.ThumbnailService;
66  import org.exoplatform.services.jcr.core.ManageableRepository;
67  import org.exoplatform.services.jcr.ext.audit.AuditService;
68  import org.exoplatform.services.jcr.ext.common.SessionProvider;
69  import org.exoplatform.services.listener.ListenerService;
70  import org.exoplatform.services.log.ExoLogger;
71  import org.exoplatform.services.log.Log;
72  import org.exoplatform.services.wcm.core.NodetypeConstant;
73  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
74  import org.exoplatform.web.application.ApplicationMessage;
75  import org.exoplatform.web.application.RequestContext;
76  import org.exoplatform.webui.config.annotation.ComponentConfig;
77  import org.exoplatform.webui.config.annotation.EventConfig;
78  import org.exoplatform.webui.core.UIApplication;
79  import org.exoplatform.webui.core.UIComponent;
80  import org.exoplatform.webui.core.UIPopupWindow;
81  import org.exoplatform.webui.event.Event;
82  import org.exoplatform.webui.ext.filter.UIExtensionFilter;
83  import org.exoplatform.webui.ext.filter.UIExtensionFilters;
84  import org.exoplatform.webui.ext.manager.UIAbstractManager;
85  import org.exoplatform.webui.ext.manager.UIAbstractManagerComponent;
86  
87  /**
88   * Created by The eXo Platform SARL
89   * Author : Hoang Van Hung
90   *          hunghvit@gmail.com
91   * Aug 6, 2009
92   */
93  
94  @ComponentConfig(
95                   events = {
96                       @EventConfig(listeners = DeleteManageComponent.DeleteActionListener.class)
97                   }
98      )
99  
100 public class DeleteManageComponent extends UIAbstractManagerComponent {
101 
102   private static final Log LOG = ExoLogger.getLogger(DeleteManageComponent.class.getName());
103 
104   private static final String DELETE_FILE_CONFIRM_TITLE = "UIDeleteFileConfirmMessage";
105   private static final String DELETE_FOLDER_CONFIRM_TITLE = "UIDeleteFolderConfirmMessage";
106   private static final String DELETE_ITEMS_CONFIRM_TITLE = "UIDeleteItemsConfirmMessage";
107 
108   private static final int GENERIC_TYPE = 1;
109   private static final int FILE_TYPE = 2;
110   private static final int FOLDER_TYPE = 3;
111 
112 
113   private static final int FOLDERS = 1; //"001";
114   private static final int FILES = 2; //"010";
115   private static final int FILES_AND_FOLDERS = 3; //"011";
116   private static final int GENERIC = 4; //"100";
117   private static final int GENERICS_AND_FOLDERS = 5; //"101";
118   private static final int GENERICS_AND_FILES = 6; //"110";
119   private static final int GENERICS_AND_FILES_AND_FOLDERS = 7; //"111";
120 
121   private static final List<UIExtensionFilter> FILTERS
122 
123       = Arrays.asList(new UIExtensionFilter[]{new IsNotLockedFilter(),
124                                               new CanDeleteNodeFilter(),
125                                               new IsNotTrashHomeNodeFilter(),
126                                               new IsNotEditingDocumentFilter(),
127                                               new IsNotMandatoryChildNode()});
128 
129   @UIExtensionFilters
130   public List<UIExtensionFilter> getFilters() {
131     return FILTERS;
132   }
133 
134   private String processRemoveMultiple(String[] nodePaths, Event<?> event) throws Exception {
135      StringBuilder trashId = new StringBuilder();
136     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
137     UIApplication uiApp = uiExplorer.getAncestorOfType(UIApplication.class);
138     Arrays.sort(nodePaths,Collections.reverseOrder());
139     for (int i = 0; i < nodePaths.length ; i++) {
140       try {
141         Node node = this.getNodeByPath(nodePaths[i]);
142         Validate.isTrue(node != null, "The ObjectId is invalid '" + nodePaths[i] + "'");
143         trashId.append(processRemoveOrMoveToTrash(nodePaths[i], node, event, true, true)).append(";");
144       } catch (PathNotFoundException path) {
145         uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.path-not-found-exception", null, ApplicationMessage.WARNING));
146         event.getRequestContext().addUIComponentToUpdateByAjax(uiApp.getUIPopupMessages());
147       } catch (Exception e) {
148         JCRExceptionManager.process(uiApp, e);
149       }
150     }
151 
152     return trashId.substring(0,trashId.length() - 1);
153   }
154 
155   private void removeAuditForNode(Node node) throws Exception {
156     UIJCRExplorer uiExplorer = this.getAncestorOfType(UIJCRExplorer.class);
157     ManageableRepository repository = uiExplorer.getRepository();
158     SessionProvider sessionProvider = uiExplorer.getSystemProvider();
159     Session session = null;
160     session = sessionProvider.getSession(node.getSession().getWorkspace().getName(), repository);
161     Node rootNode = session.getRootNode();
162     if (rootNode.hasNode("exo:audit") && rootNode.getNode("exo:audit").hasNode(node.getUUID())) {
163       rootNode.getNode("exo:audit").getNode(node.getUUID()).remove();
164       session.save();
165     }
166   }
167 
168   /**
169    * Remove or MoveToTrash
170    *
171    * @param nodePath
172    * @param node
173    * @param event
174    * @param isMultiSelect
175    * @param checkToMoveToTrash
176    * @return
177    *  0: node removed
178    * -1: move to trash failed
179    * trashId: moved to trash successfully
180    * @throws Exception
181    */
182   private String processRemoveOrMoveToTrash(String nodePath,
183                                           Node node,
184                                           Event<?> event,
185                                           boolean isMultiSelect,
186                                           boolean checkToMoveToTrash)
187                                               throws Exception {
188     String trashId="-1";
189     if (!checkToMoveToTrash || Utils.isInTrash(node)) {
190       processRemoveNode(nodePath, node, event, isMultiSelect);
191       return "0";
192     }else {
193       trashId = moveToTrash(nodePath, node, event, isMultiSelect);
194       if (!trashId.equals("-1")) {
195         //Broadcast the event when delete folder, in case deleting file, Thrash service will broadcast event 
196         ListenerService listenerService =  WCMCoreUtils.getService(ListenerService.class);
197 
198         TrashService trashService = WCMCoreUtils.getService(TrashService.class);
199         node = trashService.getNodeByTrashId(trashId);
200         if(!isDocumentNodeType(node) 
201         		&& !node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)){
202           Queue<Node> queue = new LinkedList<Node>();
203           queue.add(node);
204 
205           //Broadcast event to remove file activities
206           Node tempNode = null;
207           try {
208             while (!queue.isEmpty()) {
209               tempNode = queue.poll();
210               if (isDocumentNodeType(tempNode) || tempNode.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
211                 listenerService.broadcast(ActivityCommonService.FILE_REMOVE_ACTIVITY, tempNode.getParent(), tempNode);
212               } else {
213                 for (NodeIterator iter = tempNode.getNodes(); iter.hasNext(); ) {
214                   Node childNode = iter.nextNode();
215                   if(isDocumentNodeType(childNode) || childNode.isNodeType(NodetypeConstant.NT_UNSTRUCTURED) || 
216                       childNode.isNodeType(NodetypeConstant.NT_FOLDER))
217                     queue.add(childNode);
218                 }
219               }
220             }
221           } catch (Exception e) {
222             if (LOG.isWarnEnabled()) {
223               LOG.warn(e.getMessage());
224             }
225           }
226         }
227       }
228     }
229     return trashId;
230   }
231 
232   /**
233    * Move Node to Trash
234    * Return -1: move failed
235    * Return trashId: move successfully with trashId
236    * @param srcPath
237    * @param node
238    * @param event
239    * @param isMultiSelect
240    * @return
241    * @throws Exception
242    */
243   private String moveToTrash(String srcPath, Node node, Event<?> event, boolean isMultiSelect) throws Exception {
244     TrashService trashService = WCMCoreUtils.getService(TrashService.class);
245     AuditService auditService = WCMCoreUtils.getService(AuditService.class);
246     boolean ret = true;
247     String trashId="-1";
248     final String virtualNodePath = srcPath;
249     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
250     UIApplication uiApp = uiExplorer.getAncestorOfType(UIApplication.class);
251     try {
252       uiExplorer.addLockToken(node);
253     } catch (Exception e) {
254       JCRExceptionManager.process(uiApp, e);
255       return trashId;
256     }
257 
258     try {
259       if (node.isLocked()) {
260         LockUtil.removeLock(node);
261         node.unlock();
262       }
263       //remove audit relations 
264       if(auditService.hasHistory(node)) {
265         auditService.removeHistory(node);
266       }
267       //remove mixin auditable
268       if( node.isNodeType(Utils.EXO_AUDITABLE)){
269         node.removeMixin(Utils.EXO_AUDITABLE);
270       }
271       node.save();
272       //remove all relations that refer to this node
273       RelationsService relationService = uiApp.getApplicationComponent(RelationsService.class) ;
274       PropertyIterator iter = node.getReferences();
275       while (iter.hasNext()) {
276         Node refNode = iter.nextProperty().getParent();
277         relationService.removeRelation(refNode, node.getPath());
278       }
279 
280       if (!node.isCheckedOut())
281         throw new VersionException("node is locked, can't move to trash node :" + node.getPath());
282       if (!PermissionUtil.canRemoveNode(node))
283         throw new AccessDeniedException("access denied, can't move to trash node:" + node.getPath());
284       SessionProvider sessionProvider = uiExplorer.getSessionProvider();
285       Node currentNode = uiExplorer.getCurrentNode();
286 
287       try {
288         trashId = trashService.moveToTrash(node, sessionProvider);
289       } catch (PathNotFoundException ex) {
290         ret = false;
291       }
292       String currentPath = LinkUtils.getExistPath(currentNode, uiExplorer.getCurrentPath());
293       uiExplorer.setCurrentPath(currentPath);
294       uiExplorer.updateAjax(event);
295 
296     } catch (LockException e) {
297       if (LOG.isErrorEnabled()) {
298         LOG.error("node is locked, can't move to trash node :" + node.getPath());
299       }
300       ApplicationMessage appMessage =
301           new ApplicationMessage("UIPopupMenu.msg.can-not-remove-locked-node",
302                                  new String[] {node.getPath()}, ApplicationMessage.ERROR);
303       appMessage.setArgsLocalized(false);
304       uiApp.addMessage(appMessage);
305       uiExplorer.updateAjax(event);
306       ret = false;
307     } catch (VersionException e) {
308       if (LOG.isErrorEnabled()) {
309         LOG.error("node is checked in, can't move to trash node:" + node.getPath());
310       }
311       removeMixinEXO_RESTORE_LOCATION(node);
312       ApplicationMessage appMessage =
313           new ApplicationMessage("UIPopupMenu.msg.can-not-remove-checked-in-node",
314                                  new String[] {node.getPath()}, ApplicationMessage.ERROR);
315       appMessage.setArgsLocalized(false);
316       uiApp.addMessage(appMessage);
317       uiExplorer.updateAjax(event);
318       ret = false;
319     } catch (AccessDeniedException e) {
320       if (LOG.isErrorEnabled()) {
321         LOG.error("access denied, can't move to trash node:" + node.getPath());
322       }
323       ApplicationMessage appMessage =
324           new ApplicationMessage("UIPopupMenu.msg.access-denied-to-delete",
325                                  new String[] {node.getPath()}, ApplicationMessage.ERROR);
326       appMessage.setArgsLocalized(false);
327       uiApp.addMessage(appMessage);
328       uiExplorer.updateAjax(event);
329       ret = false;
330     } catch (Exception e) {
331       if (LOG.isErrorEnabled()) {
332         LOG.error("an unexpected error occurs", e);
333       }
334       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.unexpected-error",
335                                               new String[] {node.getPath()}, ApplicationMessage.ERROR));
336       uiExplorer.updateAjax(event);
337       ret = false;
338     }
339 
340     if (!isMultiSelect) {
341       if (uiExplorer.getCurrentPath().equals(virtualNodePath))
342         uiExplorer.setSelectNode(LinkUtils.getParentPath(virtualNodePath));
343       else
344         uiExplorer.setSelectNode(uiExplorer.getCurrentPath());
345     }
346     return (ret)?trashId:"-1";
347   }
348 
349   private void processRemoveNode(String nodePath, Node node, Event<?> event, boolean isMultiSelect)
350       throws Exception {
351     final String virtualNodePath = nodePath;
352     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
353     Node currentNode = uiExplorer.getCurrentNode();
354     Session session = node.getSession();
355     UIApplication uiApp = uiExplorer.getAncestorOfType(UIApplication.class);
356     try {
357       uiExplorer.addLockToken(node);
358     } catch (Exception e) {
359       JCRExceptionManager.process(uiApp, e);
360       return;
361     }
362     Node parentNode = node.getParent();
363     uiExplorer.addLockToken(parentNode);
364     try {
365 
366       // If node has taxonomy
367       TaxonomyService taxonomyService = uiExplorer.getApplicationComponent(TaxonomyService.class);
368       List<Node> listTaxonomyTrees = taxonomyService.getAllTaxonomyTrees();
369       List<Node> listExistedTaxonomy = taxonomyService.getAllCategories(node);
370       for (Node existedTaxonomy : listExistedTaxonomy) {
371         for (Node taxonomyTrees : listTaxonomyTrees) {
372           if(existedTaxonomy.getPath().contains(taxonomyTrees.getPath())) {
373             taxonomyService.removeCategory(node, taxonomyTrees.getName(),
374                                            existedTaxonomy.getPath().substring(taxonomyTrees.getPath().length()));
375             break;
376           }
377         }
378       }
379 
380       ActionServiceContainer actionService = getApplicationComponent(ActionServiceContainer.class);
381       actionService.removeAction(node, uiExplorer.getRepositoryName());
382       ThumbnailService thumbnailService = getApplicationComponent(ThumbnailService.class);
383       thumbnailService.processRemoveThumbnail(node);
384       NewFolksonomyService newFolksonomyService = getApplicationComponent(NewFolksonomyService.class);
385 
386       newFolksonomyService.removeTagsOfNodeRecursively(node,
387                                                        uiExplorer.getRepository()
388                                                        .getConfiguration()
389                                                        .getDefaultWorkspaceName(),
390                                                        WCMCoreUtils.getRemoteUser(),
391                                                        getGroups());
392       //trashService.removeRelations(node, uiExplorer.getSystemProvider(), uiExplorer.getRepositoryName());
393       if (PermissionUtil.canRemoveNode(node) && node.isNodeType(Utils.EXO_AUDITABLE)) {
394         removeAuditForNode(node);
395       }
396       //Remove symlinks
397       LinkManager linkManager = WCMCoreUtils.getService(LinkManager.class);
398       if(!node.isNodeType(NodetypeConstant.EXO_SYMLINK)) {
399         for(Node symlink : linkManager.getAllLinks(node, NodetypeConstant.EXO_SYMLINK)) {
400           symlink.remove();
401           symlink.getSession().save();
402         }
403       }
404       node.remove();
405       parentNode.save();
406     } catch (VersionException ve) {
407       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.remove-verion-exception", null,
408                                               ApplicationMessage.WARNING));
409 
410       uiExplorer.updateAjax(event);
411       return;
412     } catch (ReferentialIntegrityException ref) {
413       session.refresh(false);
414       uiExplorer.refreshExplorer();
415       uiApp
416       .addMessage(new ApplicationMessage(
417                                          "UIPopupMenu.msg.remove-referentialIntegrityException", null,
418                                          ApplicationMessage.WARNING));
419 
420       uiExplorer.updateAjax(event);
421       return;
422     } catch (ConstraintViolationException cons) {
423       session.refresh(false);
424       uiExplorer.refreshExplorer();
425       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.constraintviolation-exception",
426                                               null, ApplicationMessage.WARNING));
427 
428       uiExplorer.updateAjax(event);
429       return;
430     } catch (LockException lockException) {
431       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.node-locked-other-person", null,
432                                               ApplicationMessage.WARNING));
433 
434       uiExplorer.updateAjax(event);
435       return;
436     } catch (Exception e) {
437       if (LOG.isErrorEnabled()) {
438         LOG.error("an unexpected error occurs while removing the node", e);
439       }
440       JCRExceptionManager.process(uiApp, e);
441 
442       return;
443     }
444     if (!isMultiSelect) {
445       if (currentNode.getPath().equals(virtualNodePath))
446         uiExplorer.setSelectNode(LinkUtils.getParentPath(virtualNodePath));
447       else
448         uiExplorer.setSelectNode(currentNode.getPath());
449     }
450   }
451 
452   private void processRemoveMultiple(String[] nodePaths, String[] wsNames, Event<?> event)
453       throws Exception {
454     for (int i = 0; i < nodePaths.length; i++) {
455       processRemove(nodePaths[i], wsNames[i], event, true);
456     }
457   }
458 
459   private void processRemove(String nodePath, String wsName, Event<?> event, boolean isMultiSelect)
460       throws Exception {
461     if (wsName == null) {
462       wsName = getDefaultWorkspace();
463     }
464     doDelete(wsName.concat(":").concat(nodePath), event);
465   }
466 
467   private String getDefaultWorkspace() {
468     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
469     return uiExplorer.getCurrentDriveWorkspace();
470   }
471 
472   public void doDelete(String nodePath, String wsName, Event<?> event) throws Exception {
473     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
474     if (nodePath.indexOf(";") > -1) {
475       processRemoveMultiple(nodePath.split(";"), wsName.split(";"), event);
476     } else {
477       processRemove(nodePath, wsName, event, false);
478     }
479     uiExplorer.updateAjax(event);
480   }
481 
482   public void doDeleteWithoutTrash(String nodePath, Event<?> event) throws Exception {
483     doDelete(nodePath, event, false);
484   }
485 
486   public void doDelete(String nodePath, Event<?> event) throws Exception {
487     doDelete(nodePath, event, true);
488   }
489 
490   public void doDelete(String nodePath, Event<?> event, boolean checkToMoveToTrash) throws Exception {
491     RequestContext context = RequestContext.getCurrentInstance();
492     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
493     UIWorkingArea uiWorkingArea = getAncestorOfType(UIWorkingArea.class);
494     ResourceBundle res = context.getApplicationResourceBundle();
495     String deleteNotice = "";
496     String deleteNoticeParam = "";
497     String trashId = "";
498     if (nodePath.indexOf(";") > -1) {
499       trashId = processRemoveMultiple(Utils.removeChildNodes(nodePath), event);
500       if(checkToMoveToTrash) deleteNotice = "UIWorkingArea.msg.feedback-delete-multi";
501       else deleteNotice = "UIWorkingArea.msg.feedback-delete-permanently-multi";
502       deleteNoticeParam = String.valueOf(nodePath.split(";").length);
503     } else {
504       UIApplication uiApp = uiExplorer.getAncestorOfType(UIApplication.class);
505       // Prepare to remove
506       try {
507         Node node = this.getNodeByPath(nodePath);
508         if(checkToMoveToTrash) deleteNotice = "UIWorkingArea.msg.feedback-delete";
509         else deleteNotice = "UIWorkingArea.msg.feedback-delete-permanently";
510         deleteNoticeParam = StringEscapeUtils.unescapeHtml(Utils.getTitle(node));
511         if (node != null) {
512           trashId = processRemoveOrMoveToTrash(node.getPath(), node, event, false, checkToMoveToTrash);
513         }
514       } catch (PathNotFoundException path) {
515         uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.path-not-found-exception", null,
516                                                 ApplicationMessage.WARNING));
517         event.getRequestContext().addUIComponentToUpdateByAjax(uiApp.getUIPopupMessages());
518         return;
519       } catch (Exception e) {
520         JCRExceptionManager.process(uiApp, e);
521         return;
522       }
523     }
524     deleteNotice = res.getString(deleteNotice);
525     deleteNotice = deleteNotice.replace("{" + 0 + "}", deleteNoticeParam);
526     deleteNotice = deleteNotice.replace("\"", "'");
527     deleteNotice = StringEscapeUtils.escapeHtml(deleteNotice);
528     if(checkToMoveToTrash) {
529       String undoLink = getUndoLink(trashId);
530       uiWorkingArea.setDeleteNotice(deleteNotice);
531       uiWorkingArea.setNodePathDelete(undoLink);
532     } else {
533       uiWorkingArea.setWCMNotice(deleteNotice);
534     }
535     uiExplorer.updateAjax(event);
536   }
537   /**
538    * Get undo link to restore nodes that deleted
539    *
540    * @param trashId node path of nodes want to restore
541    * @throws Exception
542    */
543   private String getUndoLink(String trashId) throws Exception {
544     String undoLink = "";
545     TrashService trashService = WCMCoreUtils.getService(TrashService.class);
546     
547     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
548     PortletPreferences portletPrefs = uiExplorer.getPortletPreferences();
549 
550     String trashWorkspace = portletPrefs.getValue(Utils.TRASH_WORKSPACE, "");
551     StringBuffer sb = new StringBuffer();
552     if (trashId.indexOf(";") > -1) {
553       String[] nodePaths = trashId.split(";");
554       for(int i=0; i<nodePaths.length; i++) {        
555         trashId = nodePaths[i].substring(nodePaths[i].indexOf(":") + 1, nodePaths[i].length());
556         sb.append(trashWorkspace).append(":").append(trashService.getNodeByTrashId(trashId).getPath()).append(";");
557       }
558       undoLink = sb.toString();
559       if(undoLink.length() > 0) undoLink = undoLink.substring(0,undoLink.length()-1);
560     } else {
561       trashId = trashId.substring(trashId.indexOf(":") + 1, trashId.length());
562       Node tmpNode = trashService.getNodeByTrashId(trashId);
563       sb.append(tmpNode.getPath()).append(";");
564       undoLink = sb.toString();
565       if(undoLink.length() > 0) {
566         undoLink = undoLink.substring(0,undoLink.length()-1);
567         undoLink =  trashWorkspace + ":" +undoLink;
568       }
569     }
570     return undoLink;
571   }
572 
573   private boolean isInTrashFolder(String nodePath) throws Exception {
574     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
575     String wsName = null;
576     Session session = null;
577     String[] nodePaths = nodePath.split(";");
578     for(int i=0; i<nodePaths.length; i++) {
579       Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(nodePaths[i]);
580       if (matcher.find()) {
581         wsName = matcher.group(1);
582         nodePath = matcher.group(2);
583         session = uiExplorer.getSessionByWorkspace(wsName);
584         Node node = uiExplorer.getNodeByPath(nodePath, session, false);
585         return Utils.isInTrash(node);
586       }
587     }
588     return false;
589   }
590 
591   private boolean isDocumentNodeType(Node node) throws Exception {
592     boolean isDocument = true;
593     TemplateService templateService = WCMCoreUtils.getService(TemplateService.class);
594     isDocument = templateService.getAllDocumentNodeTypes().contains(node.getPrimaryNodeType().getName());
595     return isDocument;
596   }
597   /**
598    * Get the content type of one node
599    *
600    * @param nodePath node path of one node
601    * @throws Exception
602    */
603   private int getContentType(String nodePath) throws Exception {
604     int content_type = 1;
605     Node node = getNodeByPath(nodePath);
606     String primaryType = node.getPrimaryNodeType().getName();
607     if(node.isNodeType(NodetypeConstant.NT_FILE)) content_type = 2;
608     else if (primaryType.equals(NodetypeConstant.NT_FOLDER) || primaryType.equals(NodetypeConstant.NT_UNSTRUCTURED))
609       content_type = 3;
610     else content_type = 1;
611     return content_type;
612   }
613   /**
614    * Get the content type of multiple nodes
615    *
616    * @param nodePath node path of multiple nodes
617    * @throws Exception
618    */
619   private int getMultiContentType(String nodePath) throws Exception {
620     StringBuffer sBuffer = new StringBuffer();
621     String[] nodePaths = nodePath.split(";");
622     boolean isGeneric = false;
623     boolean isFile = false;
624     boolean isFolder = false;
625 
626     for(int i=0; i<nodePaths.length; i++) {
627       Node node = getNodeByPath(nodePaths[i]);
628       String primaryType = node.getPrimaryNodeType().getName();
629       if(node.isNodeType(NodetypeConstant.NT_FILE)) isFile = true;
630       else if (primaryType.equals(NodetypeConstant.NT_FOLDER) || primaryType.equals(NodetypeConstant.NT_UNSTRUCTURED))
631         isFolder = true;
632       else isGeneric = true;
633     }
634     if(isGeneric) sBuffer.append("1");
635     else sBuffer.append("0");
636 
637     if(isFile) sBuffer.append("1");
638     else sBuffer.append("0");
639 
640     if(isFolder) sBuffer.append("1");
641     else sBuffer.append("0");
642 
643     return Integer.parseInt(sBuffer.toString(),2);
644   }
645 
646   public static void deleteManage(Event<? extends UIComponent> event) throws Exception {
647     UIWorkingArea uiWorkingArea = event.getSource().getParent();
648     UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
649     String nodePath = event.getRequestContext().getRequestParameter(OBJECTID);
650     UIConfirmMessage uiConfirmMessage = uiWorkingArea.createUIComponent(UIConfirmMessage.class, null, null);
651     UIApplication uiApp = uiExplorer.getAncestorOfType(UIApplication.class);
652     DeleteManageComponent deleteManageComponent = uiWorkingArea.getChild(DeleteManageComponent.class);
653 
654     //get nodes that have relations referring to them
655     List<String> listNodesHaveRelations = null;
656     try {
657       listNodesHaveRelations = checkRelations(nodePath, uiExplorer);
658     } catch (PathNotFoundException pathEx) {
659       uiApp.addMessage(new ApplicationMessage("UIPopupMenu.msg.path-not-found-exception", null, ApplicationMessage.WARNING));
660 
661       return;
662     }
663 
664     boolean isInTrashFolder = deleteManageComponent.isInTrashFolder(nodePath);
665     uiConfirmMessage.setNodeInTrash(isInTrashFolder);
666     String nodeName = nodePath;
667     int contentType = 1;
668     int multiContentType = 1;
669     String message_key = "";
670     // Check and set the title for Delete Confirmation Dialog
671     if(nodePath.indexOf(";") > 0) {
672       uiConfirmMessage.setId(DELETE_ITEMS_CONFIRM_TITLE);
673       multiContentType = deleteManageComponent.getMultiContentType(nodePath);
674     } else {
675       Node node = deleteManageComponent.getNodeByPath(nodePath);
676       if(node != null)
677         nodeName = StringEscapeUtils.unescapeHtml(Utils.getTitle(node));
678       contentType = deleteManageComponent.getContentType(nodePath);
679       if(contentType == FILE_TYPE)
680         uiConfirmMessage.setId(DELETE_FILE_CONFIRM_TITLE);
681       else if (contentType == FOLDER_TYPE)
682         uiConfirmMessage.setId(DELETE_FOLDER_CONFIRM_TITLE);
683     }
684 
685     //show confirm message
686     if (listNodesHaveRelations != null && listNodesHaveRelations.size() > 0) { 
687       // there are some nodes which have relations referring to them
688       // in the deleting node list
689       // build node list to string to add into the confirm message
690       StringBuffer sb = new StringBuffer();
691       for (int i = 0; i < listNodesHaveRelations.size(); i++) {
692         sb.append("'").append(listNodesHaveRelations.get(i)).append("', ");
693       }
694       //remove "," character at the end of string
695 
696       // Node has relations means it is not in Trash folder and is not Folder
697       if (nodePath.indexOf(";") < 0) { // in case: delete one node that has relations
698         if(contentType == GENERIC_TYPE)
699           message_key = "UIWorkingArea.msg.confirm-delete-has-relations";
700         else if(contentType == FILE_TYPE)
701           message_key = "UIWorkingArea.msg.confirm-delete-file-has-relations";
702         else if (contentType == FOLDER_TYPE) {
703           message_key = "UIWorkingArea.msg.confirm-delete-folder-has-relations";
704         }
705         uiConfirmMessage.setMessageKey(message_key);
706         uiConfirmMessage.setArguments(new String[] { nodeName });
707       } else { // in case: delete multiple node have relations
708 
709         switch(multiContentType) {
710         case FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-folder-have-relations"; break;
711         case FILES: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file-have-relations"; break;
712         case FILES_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file-and-folder-have-relations";
713         break;
714         case GENERIC: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-have-relations"; break;
715         case GENERICS_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and" +
716             "-folder-have-relations"; break;
717         case GENERICS_AND_FILES: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-" +
718             "and-file-have-relations"; break;
719         case GENERICS_AND_FILES_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-" +
720             "and-file-and-folder-have-relations"; break;
721         default: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-have-relations"; break;
722         }
723 
724         uiConfirmMessage.setMessageKey(message_key);
725         uiConfirmMessage.setArguments(new String[] { Integer.toString(nodePath.split(";").length) });
726       }
727 
728     } else {  //there isn't any node which has relations referring to it in the deleting node list
729       if (isInTrashFolder) {
730         if (nodePath.indexOf(";") > -1) { // delete multi
731 
732           switch(multiContentType) {
733           case FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-folder-permanently"; break;
734           case FILES: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file-permanently"; break;
735           case FILES_AND_FOLDERS: 
736             message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file-and-folder-permanently"; break;
737           case GENERIC: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-permanently"; break;
738           case GENERICS_AND_FOLDERS: 
739             message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-folder-permanently"; break;
740           case GENERICS_AND_FILES: 
741             message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-file-permanently"; break;
742           case GENERICS_AND_FILES_AND_FOLDERS: 
743             message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-file-and-folder-permanently"; break;
744           default: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-permanently"; break;
745           }
746 
747           uiConfirmMessage.setMessageKey(message_key);
748           uiConfirmMessage.setArguments(new String[] { Integer.toString(nodePath.split(";").length) });
749         } else { // delete one
750           if(contentType == GENERIC_TYPE)
751             message_key = "UIWorkingArea.msg.confirm-delete-permanently";
752           else if(contentType == FILE_TYPE)
753             message_key = "UIWorkingArea.msg.confirm-delete-file-permanently";
754           else if(contentType == FOLDER_TYPE)
755             message_key = "UIWorkingArea.msg.confirm-delete-folder-permanently";
756           uiConfirmMessage.setMessageKey(message_key);
757           uiConfirmMessage.setArguments(new String[] { nodeName });
758         }
759       } else {
760         if (nodePath.indexOf(";") > -1) { // delete multi
761 
762           switch(multiContentType) {
763           case FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-folder"; break;
764           case FILES: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file"; break;
765           case FILES_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-file-and-folder"; break;
766           case GENERIC: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic"; break;
767           case GENERICS_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-folder"; break;
768           case GENERICS_AND_FILES: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-file"; break;
769           case GENERICS_AND_FILES_AND_FOLDERS: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic-and-file-" +
770               "and-folder"; break;
771           default: message_key = "UIWorkingArea.msg.confirm-delete-multi-nodes-generic"; break;
772           }
773           uiConfirmMessage.setMessageKey(message_key);
774           uiConfirmMessage.setArguments(new String[] { Integer.toString(nodePath.split(";").length) });
775         } else { // delete one
776           if(contentType == GENERIC_TYPE)
777             message_key = "UIWorkingArea.msg.confirm-delete";
778           else if(contentType == FILE_TYPE)
779             message_key = "UIWorkingArea.msg.confirm-delete-file";
780           else if(contentType == FOLDER_TYPE)
781             message_key = "UIWorkingArea.msg.confirm-delete-folder";
782           uiConfirmMessage.setMessageKey(message_key);
783           uiConfirmMessage.setArguments(new String[] { nodeName });
784         }
785       }
786     }
787 
788     uiConfirmMessage.setNodePath(nodePath);
789     UIPopupWindow popUp = uiExplorer.getChild(UIPopupWindow.class);
790     popUp.setUIComponent(uiConfirmMessage);
791     popUp.setShowMask(true);
792     popUp.setShow(true);
793     event.getRequestContext().addUIComponentToUpdateByAjax(popUp);
794 
795   }
796 
797   /**
798    * This function uses to get a node list that have relations referring to them in the deleting node list
799    *
800    * @param nodePath The list of nodes that user wants to delete
801    * @param uiExplorer uiExplorer
802    * @return The list of nodes that have relations referring to them
803    * @throws Exception
804    */
805   private static List<String> checkRelations(String nodePath, UIJCRExplorer uiExplorer) throws Exception{
806 
807     Node node = null;
808     String wsName = null;
809     Session session = null;
810     String[] nodePaths = nodePath.split(";");
811     RelationsService rlService = WCMCoreUtils.getService(RelationsService.class);
812     SessionProvider sessionProvider = WCMCoreUtils.getUserSessionProvider();
813     List<String> listNodesHaveRelations = new ArrayList<String>();
814     for (int i = 0; i < nodePaths.length; i++) {
815       Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(nodePaths[i]);
816       if (matcher.find()) {
817         wsName = matcher.group(1);
818         nodePath = matcher.group(2);
819         session = uiExplorer.getSessionByWorkspace(wsName);
820         node = uiExplorer.getNodeByPath(nodePath, session, false);
821         if (rlService.getRelations(node, sessionProvider).size()>0) {
822           //check references
823           listNodesHaveRelations.add(nodePath);
824         }
825       } else {
826         throw new IllegalArgumentException("The ObjectId is invalid '" + nodePath + "'");
827       }
828     }
829     return listNodesHaveRelations;
830   }
831 
832   /**
833    * Get node by node path.
834    *
835    * @param nodePath node path of specific node with syntax [workspace:node path]
836    * @return Node of specific node nath
837    * @throws Exception
838    */
839   private Node getNodeByPath(String nodePath) throws Exception {
840     UIJCRExplorer uiExplorer = this.getAncestorOfType(UIJCRExplorer.class);
841     Matcher matcher = UIWorkingArea.FILE_EXPLORER_URL_SYNTAX.matcher(nodePath);
842     if (!matcher.find()) return null;
843     String wsName = matcher.group(1);
844     nodePath = matcher.group(2);
845     Session session = uiExplorer.getSessionByWorkspace(wsName);
846     return uiExplorer.getNodeByPath(nodePath, session, false);
847   }
848 
849   private String getGroups() throws Exception {
850     StringBuilder ret = new StringBuilder();
851     for (String group : Utils.getGroups())
852       ret.append(group).append(';');
853     ret.deleteCharAt(ret.length() - 1);
854     return ret.toString();
855   }
856 
857   @Override
858   public Class<? extends UIAbstractManager> getUIAbstractManagerClass() {
859     return null;
860   }
861 
862   public static class DeleteActionListener extends UIWorkingAreaActionListener<DeleteManageComponent> {
863     public void processEvent(Event<DeleteManageComponent> event) throws Exception {
864       deleteManage(event);
865     }
866   }
867 
868   private void removeMixinEXO_RESTORE_LOCATION(Node node) throws Exception {
869     if (node.isNodeType(Utils.EXO_RESTORELOCATION)) {
870       node.removeMixin(Utils.EXO_RESTORELOCATION);
871       node.save();
872     }
873   }
874 }