View Javadoc
1   /*
2    * Copyright (C) 2003-2012 eXo Platform SAS.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (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 Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero 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 org.apache.commons.lang.StringUtils;
20  import org.exoplatform.commons.utils.*;
21  import org.exoplatform.ecm.jcr.model.Preference;
22  import org.exoplatform.ecm.webui.component.explorer.control.action.ManageVersionsActionComponent;
23  import org.exoplatform.ecm.webui.component.explorer.versions.UIActivateVersion;
24  import org.exoplatform.ecm.webui.component.explorer.versions.UIVersionInfo;
25  import org.exoplatform.ecm.webui.utils.JCRExceptionManager;
26  import org.exoplatform.ecm.webui.utils.Utils;
27  import org.exoplatform.portal.webui.util.Util;
28  import org.exoplatform.portal.webui.workspace.UIPortalApplication;
29  import org.exoplatform.services.cms.documents.VersionHistoryUtils;
30  import org.exoplatform.services.cms.link.*;
31  import org.exoplatform.services.log.ExoLogger;
32  import org.exoplatform.services.log.Log;
33  import org.exoplatform.services.security.ConversationState;
34  import org.exoplatform.services.security.IdentityConstants;
35  import org.exoplatform.services.wcm.core.NodeLocation;
36  import org.exoplatform.services.wcm.core.NodetypeConstant;
37  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
38  import org.exoplatform.web.application.ApplicationMessage;
39  import org.exoplatform.web.application.RequestContext;
40  import org.exoplatform.webui.config.annotation.ComponentConfig;
41  import org.exoplatform.webui.config.annotation.EventConfig;
42  import org.exoplatform.webui.core.*;
43  import org.exoplatform.webui.event.Event;
44  import org.exoplatform.webui.event.EventListener;
45  
46  import javax.jcr.*;
47  import java.text.DateFormat;
48  import java.text.SimpleDateFormat;
49  import java.util.*;
50  
51  /**
52   * Created by The eXo Platform SAS
53   * Author : eXoPlatform
54   *          exo@exoplatform.com
55   * Nov 29, 2012
56   */
57  @ComponentConfig (
58      template =  "app:/groovy/webui/component/explorer/UIDocumentNodeList.gtmpl",
59      events = {
60          @EventConfig(listeners = UIDocumentNodeList.ExpandNodeActionListener.class),
61          @EventConfig(listeners = UIDocumentNodeList.CollapseNodeActionListener.class),
62          @EventConfig(listeners = UIDocumentNodeList.ManageVersionsActionListener.class),
63          @EventConfig(listeners = UIDocumentNodeList.MoreActionListener.class)
64      }
65  )
66  public class UIDocumentNodeList extends UIContainer {
67  
68    private static final Log      LOG                                = ExoLogger.getLogger(UIDocumentNodeList.class.getName());
69  
70    private UIPageIterator        pageIterator_;
71  
72    private LinkManager linkManager_;
73  
74    private List dataList_;
75  
76    private int padding_;
77  
78    private boolean showMoreButton_ = true;
79  
80    public UIDocumentNodeList() throws Exception {
81      linkManager_ = WCMCoreUtils.getService(LinkManager.class);
82      addChild(ManageVersionsActionComponent.class, null, null);
83      pageIterator_ = addChild(UIPageIterator.class, null, "UIDocumentNodeListPageIterator");
84      padding_ = 0;
85    }
86  
87    @SuppressWarnings("unchecked")
88    public List<Node> getNodeChildrenList() throws Exception {
89      return NodeLocation.getNodeListByLocationList(showMoreButton_ ? dataList_ : pageIterator_.getCurrentPageData());
90    }
91  
92    public void setPageList(PageList p) throws Exception {
93      pageIterator_.setPageList(p);
94      dataList_ = new ArrayList();
95      if (p != null && p.getAvailable() > 0) {
96        dataList_.addAll(p.getPage(1));
97      }
98      updateUIDocumentNodeListChildren();
99    }
100 
101   public int getPadding() { return padding_; }
102   public void setPadding(int value) { padding_ = value; }
103 
104   public boolean isShowMoreButton() {
105     return showMoreButton_ && (pageIterator_ != null) &&
106             (pageIterator_.getPageList() != null) &&
107             (dataList_ != null) && (dataList_.size() < pageIterator_.getPageList().getAvailable());
108   }
109   public void setShowMoreButton(boolean value) { showMoreButton_ = value; }
110 
111   public void setCurrentNode(Node node) throws Exception {
112     setPageList(this.getPageList(node.getPath()));
113   }
114 
115   public void updateUIDocumentNodeListChildren() throws Exception {
116     Set<String> ids = new HashSet<String>();
117     //get all ids of UIDocumentNodeList children
118     for (UIComponent component : getChildren()) {
119       if (component instanceof UIDocumentNodeList) {
120         ids.add(component.getId());
121       }
122     }
123     //remove all UIDocumentNodeList children
124     for (String id : ids) {
125       this.removeChildById(id);
126     }
127     //add new UIDocumentNodeList children
128     for (Node node : getNodeChildrenList()) {
129       if (node instanceof NodeLinkAware) {
130         node = ((NodeLinkAware)node).getRealNode();
131       }
132       try {
133         Node targetNode = linkManager_.isLink(node) ? linkManager_.getTarget(node) : node;
134         if (targetNode.isNodeType(NodetypeConstant.NT_FOLDER) || targetNode.isNodeType(NodetypeConstant.NT_UNSTRUCTURED)) {
135           addUIDocList(getID(node));
136         }
137       } catch(ItemNotFoundException ine) {
138         continue;
139       }
140     }
141   }
142 
143   public UIPageIterator getContentPageIterator() {
144     return pageIterator_;
145   }
146 
147   public String getID(Node node) throws Exception {
148     return this.getAncestorOfType(UIDocumentInfo.class).getClass().getSimpleName() +
149            this.getClass().getSimpleName() + String.valueOf(Math.abs(node.getPath().hashCode()));
150   }
151 
152   public UIComponent addUIDocList(String id) throws Exception {
153     UIDocumentNodeList child = addChild(UIDocumentNodeList.class, null, id);
154     child.setPadding(padding_ + 1);
155     child.getContentPageIterator().setId(child.getId() + "PageIterator");
156     return child;
157   }
158 
159   /**
160    * gets the name of file
161    * @param file the file
162    * @param title the title
163    * @return name of file
164    * @throws Exception
165    */
166   public String getFileName(Node file, String title) throws Exception {
167     if (!file.isNodeType(NodetypeConstant.NT_FILE) || title == null) {
168       return title;
169     } else {
170       int index = title.lastIndexOf('.');
171       if (index != -1) {
172         return title.substring(0, index);
173       } else {
174         return title;
175       }
176     }
177   }
178 
179   /**
180    * gets the extension of file
181    * @param file the file
182    * @param title the title
183    * @return extension of file
184    * @throws Exception
185    */
186   public String getFileExtension(Node file, String title) throws Exception {
187     if (!file.isNodeType(NodetypeConstant.NT_FILE) || title == null) {
188       return "";
189     } else {
190       int index = title.lastIndexOf('.');
191       if (index != -1) {
192         return title.substring(index);
193       } else {
194         return "";
195       }
196     }
197   }
198 
199   /**
200    * gets date presentation of file
201    * @param file the file
202    * @return file date presentation
203    * @throws Exception
204    */
205   public String getFileDate(Node file) throws Exception {
206     String createdDate = this.getDatePropertyValue(file, NodetypeConstant.EXO_DATE_CREATED);
207     String modifiedDate = this.getDatePropertyValue(file, NodetypeConstant.EXO_LAST_MODIFIED_DATE);
208     return StringUtils.isEmpty(modifiedDate) ||
209             equalDates(file, NodetypeConstant.EXO_DATE_CREATED, NodetypeConstant.EXO_LAST_MODIFIED_DATE)?
210             getLabel("CreatedOn") + " " + createdDate : getLabel("Updated") + " " +  modifiedDate;
211   }
212 
213   private boolean equalDates(Node node, String p1, String p2) {
214     Calendar pr1 = null;
215     Calendar pr2 = null;
216     try {
217       pr1 = node.getProperty(p1).getDate();
218     } catch (PathNotFoundException e) {
219       pr1 = null;
220     } catch (ValueFormatException e) {
221       pr1 = null;
222     } catch (RepositoryException e) {
223       pr1 = null;
224     }
225     try {
226       pr2 = node.getProperty(p2).getDate();
227     } catch (PathNotFoundException e) {
228       pr2 = null;
229     } catch (ValueFormatException e) {
230       pr2 = null;
231     } catch (RepositoryException e) {
232       pr2 = null;
233     }
234     if ((pr1 == null) && (pr2 == null)) return true;
235     if ((pr1 == null) || (pr2 == null)) return false;
236     return Math.abs(pr1.getTimeInMillis() - pr2.getTimeInMillis()) < 3000;
237   }
238 
239   public String getDatePropertyValue(Node node, String propertyName) throws Exception {
240     try {
241       Property property = node.getProperty(propertyName);
242       if(property != null) {
243         Locale locale = Util.getUIPortal().getAncestorOfType(UIPortalApplication.class).getLocale();
244         DateFormat dateFormat = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT, locale);
245         return dateFormat.format(property.getDate().getTime());
246       }
247     } catch(PathNotFoundException PNE) {
248       return "";
249     }
250     return "";
251   }
252 
253   /**
254    * gets label
255    * @param id the id
256    * @return label
257    */
258   public String getLabel(String id)  {
259     RequestContext context = RequestContext.getCurrentInstance();
260     ResourceBundle res = context.getApplicationResourceBundle();
261     try {
262       return res.getString("UIDocumentNodeList.label." + id);
263     } catch (MissingResourceException ex) {
264       return id;
265     }
266   }
267 
268   /**
269    * gets version number of the node
270    * @param file the node
271    * @return version number
272    */
273   protected String getVersionNumber(Node file) throws Exception {
274     String currentVersion = null;
275     if (file.isNodeType(NodetypeConstant.MIX_VERSIONABLE)){
276       try {
277         if (file.isNodeType(VersionHistoryUtils.MIX_DISPLAY_VERSION_NAME) &&
278                 file.hasProperty(VersionHistoryUtils.MAX_VERSION_PROPERTY)) {
279           //Get max version ID
280           int max = (int) file.getProperty(VersionHistoryUtils.MAX_VERSION_PROPERTY).getLong();
281           currentVersion = String.valueOf(max-1);
282         }else {
283           currentVersion = file.getBaseVersion().getName();
284           if (currentVersion.contains("jcr:rootVersion")) currentVersion = "0";
285         }
286       }catch (Exception e) {
287         currentVersion ="0";
288       }
289       return "V"+currentVersion;
290     }
291     return "";
292   }
293 
294 
295   public String getAuthorName(Node file) throws Exception {
296     String userName = getAncestorOfType(UIDocumentInfo.class).getPropertyValue(file, NodetypeConstant.EXO_LAST_MODIFIER);
297     if (StringUtils.isEmpty(userName) || IdentityConstants.SYSTEM.equals(userName)) {
298       return StringUtils.EMPTY;
299     }
300     return String.format("%s %s",
301             getLabel("by"),
302             userName.equals(ConversationState.getCurrent().getIdentity().getUserId()) ? getLabel("you") : userName);
303   }
304   public String getFileSize(Node file) throws Exception {
305     return org.exoplatform.services.cms.impl.Utils.fileSize(file);
306   }
307 
308   @SuppressWarnings("unchecked")
309   private PageList<Object> getPageList(String path) throws Exception {
310     UIJCRExplorer uiExplorer = this.getAncestorOfType(UIJCRExplorer.class);
311     Preference pref = uiExplorer.getPreference();
312     
313     DocumentProviderUtils docProviderUtil = DocumentProviderUtils.getInstance();
314     if (docProviderUtil.canSortType(pref.getSortType()) && uiExplorer.getAllItemByTypeFilterMap().isEmpty()) {
315       return docProviderUtil.getPageList(
316                uiExplorer.getWorkspaceName(), 
317                path, 
318                pref, 
319                uiExplorer.getAllItemFilterMap(), 
320                uiExplorer.getAllItemByTypeFilterMap(),
321                (NodeLinkAware) ItemLinkAware.newInstance(uiExplorer.getWorkspaceName(), path, 
322                                                 uiExplorer.getNodeByPath(path, uiExplorer.getSystemSession())));
323 
324     }
325     
326     List<Node> nodeList = null;
327 
328     UIDocumentInfo uiDocInfo = this.getAncestorOfType(UIDocumentInfo.class);
329     int nodesPerPage = pref.getNodesPerPage();
330 
331     Set<String> allItemByTypeFilterMap = uiExplorer.getAllItemByTypeFilterMap();
332     if (allItemByTypeFilterMap.size() > 0)
333       nodeList = uiDocInfo.filterNodeList(uiExplorer.getChildrenList(path, !pref.isShowPreferenceDocuments()));
334     else
335       nodeList = uiDocInfo.filterNodeList(uiExplorer.getChildrenList(path, pref.isShowPreferenceDocuments()));
336 
337     ListAccess<Object> nodeAccList =
338         new ListAccessImpl<Object>(Object.class, NodeLocation.getLocationsByNodeList(nodeList));
339     return new LazyPageList<Object>(nodeAccList, nodesPerPage);
340   }
341 
342   static public class ExpandNodeActionListener extends EventListener<UIDocumentNodeList> {
343     public void execute(Event<UIDocumentNodeList> event) throws Exception {
344       UIDocumentNodeList uicomp = event.getSource();
345 
346       NodeFinder nodeFinder = uicomp.getApplicationComponent(NodeFinder.class);
347       String uri = event.getRequestContext().getRequestParameter(OBJECTID);
348       String workspaceName = event.getRequestContext().getRequestParameter("workspaceName");
349       UIApplication uiApp = uicomp.getAncestorOfType(UIApplication.class);
350       try {
351         // Manage ../ and ./
352         uri = LinkUtils.evaluatePath(uri);
353         // Just in order to check if the node exists
354         Item item = nodeFinder.getItem(workspaceName, uri);
355         if ((item instanceof Node) && Utils.isInTrash((Node) item)) {
356           return;
357         }
358 //        uiExplorer.setSelectNode(workspaceName, uri);
359         Node clickedNode = (Node)item;
360 //        UIDocumentNodeList uiDocNodeListChild = uicomp.addChild(UIDocumentNodeList.class, null,
361 //                                                                String.valueOf(clickedNode.getPath().hashCode()));
362         UIDocumentNodeList uiDocNodeListChild = uicomp.getChildById(uicomp.getID(clickedNode));
363         uiDocNodeListChild.setCurrentNode(clickedNode);
364         uicomp.getAncestorOfType(UIDocumentInfo.class).getExpandedFolders().add(uri);
365         event.getRequestContext().addUIComponentToUpdateByAjax(uiDocNodeListChild);
366       } catch(ItemNotFoundException nu) {
367         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.null-exception", null, ApplicationMessage.WARNING)) ;
368 
369         return ;
370       } catch(PathNotFoundException pa) {
371         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.path-not-found", null, ApplicationMessage.WARNING)) ;
372 
373         return ;
374       } catch(AccessDeniedException ace) {
375         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.access-denied", null, ApplicationMessage.WARNING)) ;
376 
377         return ;
378       } catch(RepositoryException e) {
379         if (LOG.isErrorEnabled()) {
380           LOG.error("Repository cannot be found");
381         }
382         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.repository-error", null,
383             ApplicationMessage.WARNING)) ;
384 
385         return ;
386       } catch (Exception e) {
387         JCRExceptionManager.process(uiApp, e);
388         return;
389       }
390     }
391   }
392 
393   static public class CollapseNodeActionListener extends EventListener<UIDocumentNodeList> {
394     public void execute(Event<UIDocumentNodeList> event) throws Exception {
395       UIDocumentNodeList uicomp = event.getSource();
396 
397       NodeFinder nodeFinder = uicomp.getApplicationComponent(NodeFinder.class);
398       String uri = event.getRequestContext().getRequestParameter(OBJECTID);
399       String workspaceName = event.getRequestContext().getRequestParameter("workspaceName");
400       UIApplication uiApp = uicomp.getAncestorOfType(UIApplication.class);
401       try {
402         // Manage ../ and ./
403         uri = LinkUtils.evaluatePath(uri);
404         // Just in order to check if the node exists
405         Item item = nodeFinder.getItem(workspaceName, uri);
406         if ((item instanceof Node) && Utils.isInTrash((Node) item)) {
407           return;
408         }
409         Node clickedNode = (Node)item;
410         UIDocumentNodeList uiDocNodeListChild = uicomp.getChildById(uicomp.getID(clickedNode));
411         uiDocNodeListChild.setPageList(EmptySerializablePageList.get());
412         uicomp.getAncestorOfType(UIDocumentInfo.class).getExpandedFolders().remove(uri);
413         event.getRequestContext().addUIComponentToUpdateByAjax(uiDocNodeListChild);
414       } catch(ItemNotFoundException nu) {
415         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.null-exception", null, ApplicationMessage.WARNING)) ;
416         return ;
417       } catch(PathNotFoundException pa) {
418         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.path-not-found", null, ApplicationMessage.WARNING)) ;
419         return ;
420       } catch(AccessDeniedException ace) {
421         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.access-denied", null, ApplicationMessage.WARNING)) ;
422         return ;
423       } catch(RepositoryException e) {
424         if (LOG.isErrorEnabled()) {
425           LOG.error("Repository cannot be found");
426         }
427         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.repository-error", null,
428             ApplicationMessage.WARNING)) ;
429         return ;
430       } catch (Exception e) {
431         JCRExceptionManager.process(uiApp, e);
432         return;
433       }
434     }
435   }
436 
437 
438   public static class ManageVersionsActionListener extends EventListener<UIDocumentNodeList> {
439     public void execute(Event<UIDocumentNodeList> event) throws Exception {
440       NodeFinder nodeFinder = event.getSource().getApplicationComponent(NodeFinder.class);
441       UIJCRExplorer uiExplorer = event.getSource().getAncestorOfType(UIJCRExplorer.class);
442       UIPopupContainer UIPopupContainer = uiExplorer.getChild(UIPopupContainer.class);
443       String uri = event.getRequestContext().getRequestParameter(OBJECTID);
444       String workspaceName = event.getRequestContext().getRequestParameter("workspaceName");
445       // Manage ../ and ./
446       uri = LinkUtils.evaluatePath(uri);
447       // Just in order to check if the node exists
448       Node currentNode = (Node)nodeFinder.getItem(workspaceName, uri);
449       uiExplorer.setIsHidePopup(false);
450       if (currentNode.canAddMixin(Utils.MIX_VERSIONABLE)) {
451         UIPopupContainer.activate(UIActivateVersion.class, 400);
452         event.getRequestContext().addUIComponentToUpdateByAjax(UIPopupContainer);
453       } else if (currentNode.isNodeType(Utils.MIX_VERSIONABLE)) {
454         UIWorkingArea uiWorkingArea = uiExplorer.getChild(UIWorkingArea.class);
455         UIDocumentWorkspace uiDocumentWorkspace = uiWorkingArea.getChild(UIDocumentWorkspace.class);
456         UIVersionInfo uiVersionInfo = uiDocumentWorkspace.getChild(UIVersionInfo.class);
457         uiVersionInfo.setCurrentNode(currentNode);
458         uiVersionInfo.setRootOwner(currentNode.getProperty("exo:lastModifier").getString());
459         uiVersionInfo.activate();
460         uiDocumentWorkspace.setRenderedChild(UIVersionInfo.class);
461         event.getRequestContext().addUIComponentToUpdateByAjax(uiDocumentWorkspace);
462       }
463     }
464   }
465 
466   public static class MoreActionListener extends EventListener<UIDocumentNodeList> {
467     public void execute(Event<UIDocumentNodeList> event) throws Exception {
468       UIDocumentNodeList uicomp = event.getSource();
469 
470       UIApplication uiApp = uicomp.getAncestorOfType(UIApplication.class);
471       try {
472         int currentPage = uicomp.dataList_.size() / uicomp.pageIterator_.getPageList().getPageSize();
473         uicomp.dataList_.addAll(uicomp.pageIterator_.getPageList().getPage(currentPage + 1));
474         event.getRequestContext().addUIComponentToUpdateByAjax(uicomp);
475       } catch(AccessDeniedException ace) {
476         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.access-denied", null, ApplicationMessage.WARNING)) ;
477         return ;
478       } catch(RepositoryException e) {
479         if (LOG.isErrorEnabled()) {
480           LOG.error("Repository cannot be found");
481         }
482         uiApp.addMessage(new ApplicationMessage("UIDocumentInfo.msg.repository-error", null,
483             ApplicationMessage.WARNING)) ;
484         return ;
485       } catch (Exception e) {
486         JCRExceptionManager.process(uiApp, e);
487         return;
488       }
489     }
490   }
491 
492 }