View Javadoc
1   /*
2    * Copyright (C) 2003-2007 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  package org.exoplatform.ecm.webui.component.explorer;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.regex.Pattern;
25  
26  import javax.jcr.ItemNotFoundException;
27  import javax.jcr.Node;
28  import javax.jcr.PathNotFoundException;
29  import javax.jcr.RepositoryException;
30  import javax.jcr.Session;
31  import javax.jcr.Value;
32  
33  import org.apache.commons.lang.StringUtils;
34  import org.exoplatform.ecm.webui.component.explorer.control.UIActionBar;
35  import org.exoplatform.ecm.webui.component.explorer.popup.actions.UIDocumentFormController;
36  import org.exoplatform.ecm.webui.component.explorer.popup.actions.UISelectDocumentForm;
37  import org.exoplatform.ecm.webui.component.explorer.rightclick.manager.RestoreFromTrashManageComponent;
38  import org.exoplatform.ecm.webui.component.explorer.sidebar.UISideBar;
39  import org.exoplatform.ecm.webui.component.explorer.sidebar.UITreeExplorer;
40  import org.exoplatform.ecm.webui.utils.Utils;
41  import org.exoplatform.services.cms.actions.ActionServiceContainer;
42  import org.exoplatform.services.cms.link.NodeFinder;
43  import org.exoplatform.services.cms.link.NodeLinkAware;
44  import org.exoplatform.services.jcr.RepositoryService;
45  import org.exoplatform.services.jcr.core.ManageableRepository;
46  import org.exoplatform.services.log.ExoLogger;
47  import org.exoplatform.services.log.Log;
48  import org.exoplatform.services.security.Identity;
49  import org.exoplatform.services.security.IdentityConstants;
50  import org.exoplatform.services.security.IdentityRegistry;
51  import org.exoplatform.services.security.MembershipEntry;
52  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
53  import org.exoplatform.webui.application.WebuiRequestContext;
54  import org.exoplatform.webui.config.annotation.ComponentConfig;
55  import org.exoplatform.webui.config.annotation.ComponentConfigs;
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.core.UIContainer;
60  import org.exoplatform.webui.core.UIRightClickPopupMenu;
61  import org.exoplatform.webui.event.Event;
62  import org.exoplatform.webui.event.EventListener;
63  import org.exoplatform.webui.ext.UIExtension;
64  import org.exoplatform.webui.ext.UIExtensionManager;
65  import org.exoplatform.webui.ext.manager.UIAbstractManagerComponent;
66  
67  
68  /**
69   * Created by The eXo Platform SARL Author : Tran The Trong trongtt@gmail.com
70   * July 3, 2006 10:07:15 AM
71   */
72  @ComponentConfigs( {
73    @ComponentConfig(template = "app:/groovy/webui/component/explorer/UIWorkingArea.gtmpl",
74        events = {@EventConfig(listeners = UIWorkingArea.RefreshActionListener.class)}),
75        @ComponentConfig(
76                         type = UIRightClickPopupMenu.class,
77                         id = "ECMContextMenu",
78                         template = "app:/groovy/webui/component/explorer/UIRightClickPopupMenu.gtmpl"
79            )
80  })
81  public class UIWorkingArea extends UIContainer {
82  
83    /**
84     * Logger.
85     */
86    private static final Log                 LOG                      = ExoLogger.getLogger(UIWorkingArea.class.getName());
87  
88    public static final Pattern              FILE_EXPLORER_URL_SYNTAX = Pattern.compile("([^:/]+):(/.*)");
89  
90    public static final String               WS_NAME                  = "workspaceName";
91  
92    public static final String               EXTENSION_TYPE           = "org.exoplatform.ecm.dms.UIWorkingArea";
93  
94    public static final String               ITEM_CONTEXT_MENU        = "ItemContextMenu";
95  
96    public static final String               MULTI_ITEM_CONTEXT_MENU  = "MultiItemContextMenu";
97  
98    public static final String               GROUND_CONTEXT_MENU      = "GroundContextMenu";
99  
100   public static final String               ITEM_GROUND_CONTEXT_MENU = "ItemGroundContextMenu";
101 
102   public static final String               MOVE_NODE                = "MoveNode";
103 
104   public static final String               CREATE_LINK              = "CreateLink";
105 
106   public static final String               CUSTOM_ACTIONS           = "CustomActions";
107 
108   public static final String               PERMLINK                 = "PermlinkContextMenu";
109   public static final String               PERM_LINK_ACTION         = "Permlink";
110 
111   private String nodePathDelete = "";
112   private String deleteNotice = "";
113   private String wcmNotice = "";
114 
115   public void setNodePathDelete(String nodePathDelete) {
116     this.nodePathDelete = nodePathDelete;
117   }
118 
119   public String getNodePathDelete() {
120     return nodePathDelete;
121   }
122 
123   public String getWCMNotice() {
124     return wcmNotice;
125   }
126 
127   public void setWCMNotice(String wcmNotice) {
128     this.wcmNotice = wcmNotice;
129   }
130 
131   public void setDeleteNotice(String deleteNotice) {
132     this.deleteNotice = deleteNotice;
133   }
134 
135   public String getDeleteNotice() {
136     return this.deleteNotice;
137   }
138 
139   public static final String               REFRESH_ACTION           = "Refresh";
140 
141   public static final String               RENAME_ACTION           = "Rename";
142 
143 
144   private List<UIAbstractManagerComponent> managers                 =
145       Collections.synchronizedList(new ArrayList<UIAbstractManagerComponent>());
146 
147   public UIWorkingArea() throws Exception {
148     addChild(UIRightClickPopupMenu.class, "ECMContextMenu", null);
149     addChild(UISideBar.class, null, null);
150     addChild(UIActionBar.class, null, null) ;
151     addChild(UISelectDocumentTemplateTitle.class, null, null);
152     addChild(UIDocumentWorkspace.class, null, null);
153     addChild(UIDrivesArea.class, null, null).setRendered(false);
154   }
155 
156   private List<UIExtension> getUIExtensionList() {
157     UIExtensionManager manager = getApplicationComponent(UIExtensionManager.class);
158     return manager.getUIExtensions(EXTENSION_TYPE);
159   }
160 
161   public synchronized UITreeExplorer getTreeExplorer() {
162     UISideBar uiSideBar = getChild(UISideBar.class);
163     return uiSideBar.getChild(UITreeExplorer.class);
164   }
165 
166   public void initialize() throws Exception {
167     List<UIExtension> extensions = getUIExtensionList();
168     if (extensions == null) {
169       return;
170     }
171     managers.clear();
172     for (UIExtension extension : extensions) {
173       UIComponent component = addUIExtension(extension, null);
174       if (component !=null && !managers.contains(component))
175         managers.add((UIAbstractManagerComponent)component);
176     }
177   }
178 
179   private UIComponent addUIExtension(UIExtension extension, Map<String, Object> context) throws Exception {
180     UIExtensionManager manager = getApplicationComponent(UIExtensionManager.class);
181     UIComponent component = manager.addUIExtension(extension, context, this);
182     if(component == null) return null;
183     synchronized(component) {
184       if (component instanceof UIAbstractManagerComponent) {
185         // You can access to the given extension and the extension is valid
186         UIAbstractManagerComponent uiAbstractManagerComponent = (UIAbstractManagerComponent) component;
187         uiAbstractManagerComponent.setUIExtensionName(extension.getName());
188         uiAbstractManagerComponent.setUIExtensionCategory(extension.getCategory());
189         return component;
190       } else if (component != null) {
191         // You can access to the given extension but the extension is not valid
192         if (LOG.isWarnEnabled()) {
193           LOG.warn("All the extension '" + extension.getName() + "' of type '" + EXTENSION_TYPE
194                    + "' must be associated to a component of type " + UIAbstractManagerComponent.class);
195         }
196         removeChild(component.getClass());
197       }
198     }
199     return null;
200   }
201 
202   public RestoreFromTrashManageComponent getThrashManageComponent(){
203     for(UIAbstractManagerComponent c : getManagers()){
204       if(c instanceof RestoreFromTrashManageComponent){
205         return (RestoreFromTrashManageComponent)c;
206       }
207     }
208     return null;
209   }
210   public List<UIAbstractManagerComponent> getManagers() {
211     List<UIAbstractManagerComponent> managers = new ArrayList<UIAbstractManagerComponent>();
212     managers.addAll(this.managers);
213     return managers;
214   }
215 
216   public void unregister(UIAbstractManagerComponent component) {
217     managers.remove(component);
218   }
219 
220   //Should use this method to check for when execute Actions in Working Area instead in UIEditingTagsForm (line 120)
221   public boolean isShowSideBar() throws Exception {
222     UIJCRExplorer jcrExplorer = getParent();
223     return jcrExplorer.getPreference().isShowSideBar() && getAncestorOfType(UIJCRExplorerPortlet.class).isShowSideBar();
224   }
225 
226   public void setShowSideBar(boolean b) throws Exception {
227     UIJCRExplorer jcrExplorer = getParent();
228     jcrExplorer.getPreference().setShowSideBar(b);
229   }
230 
231   public Node getNodeByUUID(String uuid) throws Exception {
232     ManageableRepository repo = getApplicationComponent(RepositoryService.class).getCurrentRepository();
233     String workspace = repo.getConfiguration().getDefaultWorkspaceName();
234     return getNodeByUUID(uuid, workspace);
235   }
236 
237   public Node getNodeByUUID(String uuid, String workspaceName) throws Exception {
238     ManageableRepository repo = getApplicationComponent(RepositoryService.class).getCurrentRepository();
239     Session session = WCMCoreUtils.getSystemSessionProvider().getSession(workspaceName, repo);
240     return session.getNodeByUUID(uuid);
241   }
242 
243   public boolean isPreferenceNode(Node node) {
244     return getAncestorOfType(UIJCRExplorer.class).isPreferenceNode(node);
245   }
246 
247   public String getVersionNumber(Node node) throws RepositoryException {
248     if (!Utils.isVersionable(node))
249       return "-";
250     return node.getBaseVersion().getName();
251   }
252 
253   public boolean isJcrViewEnable() throws Exception {
254     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
255     return uiExplorer.getPreference().isJcrEnable();
256   }
257 
258   private Map<String, Object> createContext(Node currentNode) throws Exception {
259     Map<String, Object> context = new HashMap<String, Object>();
260     WebuiRequestContext requestContext =  WebuiRequestContext.getCurrentInstance() ;
261     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
262     UIApplication uiApp = getAncestorOfType(UIApplication.class);
263     context.put(Node.class.getName(), currentNode);
264     context.put(UIWorkingArea.class.getName(), this);
265     context.put(UIApplication.class.getName(), uiApp);
266     context.put(UIJCRExplorer.class.getName(), uiExplorer);
267     context.put(WebuiRequestContext.class.getName(), requestContext);
268     return context;
269   }
270 
271   List<UIComponent> getGroundActionsExtensionList() throws Exception {
272     List<UIComponent> uiGroundActionList = new ArrayList<UIComponent>();
273     List<UIExtension> uiExtensionList = getUIExtensionList();
274     UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
275     Node currentNode = uiExplorer.getCurrentNode();
276     UIComponent uiAddedActionManage;
277     for (UIExtension uiextension : uiExtensionList) {
278       if (GROUND_CONTEXT_MENU.equals(uiextension.getCategory())
279           || ITEM_GROUND_CONTEXT_MENU.equals(uiextension.getCategory())) {
280         uiAddedActionManage = addUIExtension(uiextension, createContext(currentNode));
281         if (uiAddedActionManage != null) {
282           if (!uiGroundActionList.contains(uiAddedActionManage))
283             uiGroundActionList.add(uiAddedActionManage);
284         }
285       }
286     }
287     return uiGroundActionList;
288   }
289 
290   List<UIComponent> getMultiActionsExtensionList() throws Exception {
291     List<UIComponent> uiActionList = new ArrayList<UIComponent>();
292     List<UIExtension> uiExtensionList = getUIExtensionList();
293     UIComponent uiAddedActionManage;
294     for (UIExtension uiextension : uiExtensionList) {
295       if (ITEM_CONTEXT_MENU.equals(uiextension.getCategory())
296           || ITEM_GROUND_CONTEXT_MENU.equals(uiextension.getCategory())
297           || MULTI_ITEM_CONTEXT_MENU.equals(uiextension.getCategory())) {
298         uiAddedActionManage = addUIExtension(uiextension, null);
299         if (uiAddedActionManage != null) {
300           if (!uiActionList.contains(uiAddedActionManage))
301             uiActionList.add(uiAddedActionManage);
302         }
303       }
304     }
305     return uiActionList;
306   }
307 
308   public String getActionsExtensionList(Node node) throws Exception {
309     StringBuffer actionsList = new StringBuffer(1024);
310     List<UIExtension> uiExtensionList = getUIExtensionList();
311     UIComponent uiAddedActionManage;
312     try {
313       NodeFinder nodeFinder = getApplicationComponent(NodeFinder.class);
314       nodeFinder.getItem(getAncestorOfType(UIJCRExplorer.class).getSession(), node.getPath());
315     } catch(PathNotFoundException pne) {
316       return "";
317     }
318     for (UIExtension uiextension : uiExtensionList) {
319       if (uiextension.getCategory().startsWith(ITEM_CONTEXT_MENU)
320           || ITEM_GROUND_CONTEXT_MENU.equals(uiextension.getCategory())) {
321         uiAddedActionManage = addUIExtension(uiextension, createContext(node));
322         if (uiAddedActionManage != null) {
323           actionsList.append(uiextension.getName()).append(",");
324         }
325       }
326     }
327     if (actionsList.length() > 0) {
328       return actionsList.substring(0, actionsList.length() - 1);
329     }
330     return actionsList.toString();
331   }
332 
333   public UIComponent getJCRMoveAction() throws Exception {
334     UIExtensionManager manager = getApplicationComponent(UIExtensionManager.class);
335     UIExtension extension = manager.getUIExtension(EXTENSION_TYPE, MOVE_NODE);
336     return addUIExtension(extension, null);
337   }
338 
339   public UIComponent getCreateLinkAction() throws Exception {
340     UIExtensionManager manager = getApplicationComponent(UIExtensionManager.class);
341     UIExtension extension = manager.getUIExtension(EXTENSION_TYPE, CREATE_LINK);
342     return addUIExtension(extension, null);
343   }
344 
345   public UIComponent getPermlink(Node node) throws Exception {
346     UIComponent uicomponent = null;
347     List<UIExtension> uiExtensionList = getUIExtensionList();
348     for (UIExtension uiextension : uiExtensionList) {
349       if (PERMLINK.equals(uiextension.getCategory())) {
350         uicomponent = addUIExtension(uiextension, createContext(node));
351       }
352     }
353     return uicomponent;
354   }
355 
356   public UIComponent getCustomAction() throws Exception {
357     UIComponent uicomponent = null;
358     List<UIExtension> uiExtensionList = getUIExtensionList();
359     for (UIExtension uiextension : uiExtensionList) {
360       if (CUSTOM_ACTIONS.equals(uiextension.getCategory())) {
361         uicomponent = addUIExtension(uiextension, null);
362       }
363     }
364     return uicomponent;
365   }
366 
367   private boolean hasPermission(String userName, Value[] roles) throws Exception {
368     IdentityRegistry identityRegistry = getApplicationComponent(IdentityRegistry.class);
369     if (IdentityConstants.SYSTEM.equalsIgnoreCase(userName)) {
370       return true;
371     }
372     Identity identity = identityRegistry.getIdentity(userName);
373     if (identity == null) {
374       return false;
375     }
376     for (int i = 0; i < roles.length; i++) {
377       String role = roles[i].getString();
378       if ("*".equalsIgnoreCase(role))
379         return true;
380       MembershipEntry membershipEntry = MembershipEntry.parse(role);
381       if (membershipEntry == null)
382         return false;
383       if (identity.isMemberOf(membershipEntry)) {
384         return true;
385       }
386     }
387     return false;
388   }
389 
390   public List<Node> getCustomActions(Node node) throws Exception {
391     if (node instanceof NodeLinkAware) {
392       NodeLinkAware nodeLA = (NodeLinkAware) node;
393       try {
394         node = nodeLA.getTargetNode().getRealNode();
395       } catch (Exception e) {
396         // The target of the link is not reachable
397       }
398     }
399     List<Node> safeActions = new ArrayList<Node>();
400     WebuiRequestContext context = WebuiRequestContext.getCurrentInstance();
401     String userName = context.getRemoteUser();
402     ActionServiceContainer actionContainer = getApplicationComponent(ActionServiceContainer.class);
403     List<Node> unsafeActions = actionContainer.getCustomActionsNode(node, "read");
404     if (unsafeActions == null)
405       return new ArrayList<Node>();
406     for (Node actionNode : unsafeActions) {
407       Value[] roles = actionNode.getProperty(Utils.EXO_ROLES).getValues();
408       if (hasPermission(userName, roles))
409         safeActions.add(actionNode);
410     }
411     return safeActions;
412   }
413 
414   /**
415    * Gets the title.
416    *
417    * @param node the node
418    *
419    * @return the title
420    *
421    * @throws Exception the exception
422    */
423   public String getTitle(Node node) throws Exception {
424     return Utils.getTitle(node);
425   }
426 
427   public void processRender(WebuiRequestContext context) throws Exception {
428     UIJCRExplorerPortlet uiPortlet = getAncestorOfType(UIJCRExplorerPortlet.class);
429     UIActionBar uiActionBar = findFirstComponentOfType(UIActionBar.class);
430     uiActionBar.setRendered(uiPortlet.isShowActionBar());
431     UISelectDocumentTemplateTitle uiTemplateTitle = findFirstComponentOfType(UISelectDocumentTemplateTitle.class);
432     boolean isUITemplateTitleRendered = isUISelectDocumentTemplateTitleRendered();
433     uiTemplateTitle.setRendered(isUITemplateTitleRendered);
434     if(!context.useAjax()) {
435       if (isShowSideBar()) {
436         UITreeExplorer uiTreeExplorer = this.findFirstComponentOfType(UITreeExplorer.class);
437         if (uiTreeExplorer != null) uiTreeExplorer.buildTree();
438       }
439     }
440     super.processRender(context);
441   }
442 
443   public boolean isUISelectDocumentTemplateTitleRendered()  {
444     UIDocumentFormController uiDocumentController = findFirstComponentOfType(UIDocumentFormController.class);
445     boolean isUITemplateTitleRendered = 
446         (uiDocumentController != null
447         && uiDocumentController.isRendered()
448         && uiDocumentController.getChild(UISelectDocumentForm.class).isRendered());
449     return isUITemplateTitleRendered;
450   }
451 
452   /**
453    * Refresh UIWorkingArea after renaming.UIPresentationContainer
454    */
455   public static class RefreshActionListener extends EventListener<UIWorkingArea> {
456     public void execute(Event<UIWorkingArea> event) throws Exception {
457       UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
458 
459       // Get path before renaming
460       String pathBeforeRename = event.getRequestContext().getRequestParameter("oldPath");
461 
462       // Get path after renaming
463       String renamedNodeUUID = event.getRequestContext().getRequestParameter("uuid");
464       String pathAfterRename = null;
465       Node renamedNode = null;
466       try {
467         renamedNode = uiExplorer.getSession().getNodeByUUID(renamedNodeUUID);
468       } catch (ItemNotFoundException e) {
469         // Try to find node in other workspaces
470         String[] workspaceNames = uiExplorer.getRepository().getWorkspaceNames();
471         String currentWorkSpaceName = uiExplorer.getWorkspaceName();
472         for (String workspaceName : workspaceNames) {
473           if (!workspaceName.equals(currentWorkSpaceName)) {
474             try {
475               renamedNode = uiExplorer.getSessionByWorkspace(workspaceName).getNodeByUUID(renamedNodeUUID);
476               break;
477             } catch (ItemNotFoundException infE) {
478               renamedNode = null;
479             }
480           }
481         }
482       }
483       if (renamedNode != null) {
484         pathAfterRename = renamedNode.getPath();
485       } else {
486         LOG.warn("Can not find renamed node with old path: [%s]", pathBeforeRename);
487         return;
488       }
489 
490       // Update content explorer
491       String currentPath = uiExplorer.getCurrentPath();
492       if (currentPath.equals(pathBeforeRename)) {
493         uiExplorer.setCurrentPath(pathAfterRename) ;
494       } else if(currentPath.startsWith(pathBeforeRename)) {
495         uiExplorer.setCurrentPath(pathAfterRename + currentPath.replace(pathBeforeRename, StringUtils.EMPTY));
496       }
497       uiExplorer.updateAjax(event);
498     }
499   }
500 }