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.versions;
18  
19  import java.io.Serializable;
20  import java.util.ArrayList;
21  import java.util.Comparator;
22  import java.util.List;
23  
24  import javax.jcr.Node;
25  import javax.jcr.ReferentialIntegrityException;
26  import javax.jcr.RepositoryException;
27  import javax.jcr.version.Version;
28  import javax.jcr.version.VersionException;
29  import javax.jcr.version.VersionHistory;
30  
31  import org.apache.commons.lang.StringUtils;
32  
33  import org.exoplatform.commons.utils.LazyPageList;
34  import org.exoplatform.commons.utils.ListAccess;
35  import org.exoplatform.commons.utils.ListAccessImpl;
36  import org.exoplatform.ecm.jcr.model.VersionNode;
37  import org.exoplatform.ecm.webui.component.explorer.UIDocumentWorkspace;
38  import org.exoplatform.ecm.webui.component.explorer.UIJCRExplorer;
39  import org.exoplatform.ecm.webui.utils.Utils;
40  import org.exoplatform.services.cache.CacheService;
41  import org.exoplatform.services.cache.ExoCache;
42  import org.exoplatform.services.cms.documents.AutoVersionService;
43  import org.exoplatform.services.cms.documents.DocumentService;
44  import org.exoplatform.services.cms.documents.VersionHistoryUtils;
45  import org.exoplatform.services.cms.jcrext.activity.ActivityCommonService;
46  import org.exoplatform.services.jcr.core.ManageableRepository;
47  import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
48  import org.exoplatform.services.listener.ListenerService;
49  import org.exoplatform.services.log.ExoLogger;
50  import org.exoplatform.services.log.Log;
51  import org.exoplatform.services.pdfviewer.ObjectKey;
52  import org.exoplatform.services.pdfviewer.PDFViewerService;
53  import org.exoplatform.services.security.ConversationState;
54  import org.exoplatform.services.security.MembershipEntry;
55  import org.exoplatform.services.wcm.core.NodeLocation;
56  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
57  import org.exoplatform.wcm.connector.viewer.PDFViewerRESTService;
58  import org.exoplatform.web.application.ApplicationMessage;
59  import org.exoplatform.webui.config.annotation.ComponentConfig;
60  import org.exoplatform.webui.config.annotation.EventConfig;
61  import org.exoplatform.webui.core.UIApplication;
62  import org.exoplatform.webui.core.UIComponent;
63  import org.exoplatform.webui.core.UIContainer;
64  import org.exoplatform.webui.core.UIPageIterator;
65  import org.exoplatform.webui.event.Event;
66  import org.exoplatform.webui.event.EventListener;
67  
68  /**
69   * Created by The eXo Platform SARL
70   * Implement: lxchiati
71   *            lebienthuy@gmail.com
72   * July 3, 2006
73   * 10:07:15 AM
74   */
75  
76  @ComponentConfig(
77      template = "app:/groovy/webui/component/explorer/versions/UIVersionInfo.gtmpl",
78      events = {
79          @EventConfig(listeners = UIVersionInfo.SelectActionListener.class),
80          @EventConfig(listeners = UIVersionInfo.RestoreVersionActionListener.class, confirm = "UIVersionInfo.msg.confirm-restore"),
81          @EventConfig(listeners = UIVersionInfo.CompareVersionActionListener.class, csrfCheck = false),
82          @EventConfig(listeners = UIVersionInfo.DeleteVersionActionListener.class, confirm = "UIVersionInfo.msg.confirm-delete"),
83          @EventConfig(listeners = UIVersionInfo.CloseActionListener.class),
84          @EventConfig(listeners = UIVersionInfo.AddSummaryActionListener.class)
85      }
86  )
87  
88  public class UIVersionInfo extends UIContainer  {
89    private static final Log LOG  = ExoLogger.getLogger(UIVersionInfo.class.getName());
90  
91    protected VersionNode rootVersion_ ;
92    protected String rootOwner_;
93    protected String rootVersionNum_;
94    protected VersionNode curentVersion_;
95    protected NodeLocation node_ ;
96    private UIPageIterator uiPageIterator_ ;
97    private List<VersionNode> listVersion = new ArrayList<VersionNode>() ;
98  
99    private static final String CACHE_NAME = "ecms.PDFViewerRestService";
100 
101 
102   public UIVersionInfo() throws Exception {
103     uiPageIterator_ = addChild(UIPageIterator.class, null, "VersionInfoIterator").setRendered(false);
104   }
105 
106   public UIPageIterator getUIPageIterator() { return uiPageIterator_; }
107 
108   @SuppressWarnings("rawtypes")
109   public List getListRecords() throws Exception { return uiPageIterator_.getCurrentPageData(); }
110 
111   public void updateGrid() throws Exception {
112     listVersion.clear();
113     Node currentNode = getCurrentNode();
114     rootVersion_ = new VersionNode(currentNode, currentNode.getSession());
115     curentVersion_ = rootVersion_;
116 
117     listVersion = getNodeVersions(getRootVersionNode().getChildren());
118     VersionNode currentNodeTuple = new VersionNode(currentNode, currentNode.getSession());
119     if(!listVersion.isEmpty()) {
120       int lastVersionNum = Integer.parseInt(listVersion.get(0).getName());
121       setRootVersionNum(String.valueOf(++lastVersionNum));
122     } else {
123       setRootVersionNum("1");
124     }
125     listVersion.add(0, currentNodeTuple);
126 
127     ListAccess<VersionNode> recordList = new ListAccessImpl<VersionNode>(VersionNode.class, listVersion);
128     LazyPageList<VersionNode> dataPageList = new LazyPageList<VersionNode>(recordList, 10);
129     uiPageIterator_.setPageList(dataPageList);
130   }
131 
132   public String getTitle(Node node) throws Exception {
133     return org.exoplatform.ecm.webui.utils.Utils.getTitle(node);
134   }
135 
136   private boolean isRestoredVersions(List<VersionNode> list)  {
137     try {
138       for (int i = 0; i < list.size(); i++) {
139         if (getVersionLabels(list.get(i)).length > 0) {
140           if (isRestoredLabel(getVersionLabels(list.get(i))[0])) return true;
141         }
142       }
143     } catch (Exception e) {
144       return false;
145     }
146     return false;
147   }
148 
149   private boolean isRestoredLabel(String label) {
150     try {
151       String from = label.substring(label.indexOf("_") - 1).split("_")[0];
152       String to = label.substring(label.indexOf("_") - 1).split("_")[1];
153       Integer.parseInt(from);
154       Integer.parseInt(to);
155       return true;
156     } catch (Exception e) {
157       return false;
158     }
159   }
160 
161   public String[] getVersionLabels(VersionNode version) throws Exception {
162     VersionHistory vH = NodeLocation.getNodeByLocation(node_).getVersionHistory();
163     String[] labels;
164     if (StringUtils.isNotBlank(version.getName()) && !getRootVersionNum().equals(version.getName())) {
165       Version versionNode = vH.getVersion(version.getName());
166       labels = vH.getVersionLabels(versionNode);
167     } else {
168       labels= vH.getVersionLabels(vH.getRootVersion());
169     }
170     return labels;
171   }
172 
173   public boolean isBaseVersion(VersionNode versionNode) throws Exception {
174     if (!isRestoredVersions(listVersion)) {
175       return isRootVersion(versionNode);
176     } else {
177       return versionNode.getPath().equals(getCurrentNode().getPath());
178     }
179   }
180 
181   public boolean hasPermission(Node node) throws Exception {
182     if (getCurrentNode().getPath().startsWith("/Groups/spaces")) {
183       MembershipEntry mem = new MembershipEntry("/spaces/" + getCurrentNode().getPath().split("/")[3], "manager");
184       return (ConversationState.getCurrent().getIdentity().getMemberships().contains(mem)
185           || ConversationState.getCurrent().getIdentity().getUserId().equals(node.getProperty("exo:lastModifier").getString()));
186 
187     } else {
188       return true;
189     }
190   }
191 
192   public boolean isRootVersion(VersionNode versionNode) throws Exception {
193     return (versionNode.getUUID().equals(getCurrentNode().getUUID()));
194   }
195 
196   public VersionNode getRootVersionNode() throws Exception {  return rootVersion_ ; }
197 
198   public String getRootOwner() throws Exception {  return rootOwner_ ; }
199 
200   public void setRootOwner(String user) {  this.rootOwner_ = user; }
201 
202   private List<VersionNode> getNodeVersions(List<VersionNode> children) throws Exception {
203     List<VersionNode> child = new ArrayList<VersionNode>() ;
204     for(int i = 0; i < children.size(); i ++){
205       listVersion.add(children.get(i));
206       child = children.get(i).getChildren() ;
207       if(!child.isEmpty()) getNodeVersions(child) ;
208     }
209     listVersion.sort(new Comparator<VersionNode>() {
210       @Override
211       public int compare(VersionNode v1, VersionNode v2) {
212         try {
213           if (Integer.parseInt(v1.getName()) < Integer.parseInt(v2.getName()))
214             return 1;
215           else
216             return 0;
217         }catch (Exception e) {
218           return 0;
219         }
220       }
221     });
222     return listVersion;
223   }
224 
225   public void activate() {
226     try {
227       UIJCRExplorer uiExplorer = getAncestorOfType(UIJCRExplorer.class);
228       if (node_ == null) {
229         node_ = NodeLocation.getNodeLocationByNode(uiExplorer.getCurrentNode());
230       }
231       updateGrid();
232     } catch (Exception e) {
233       LOG.error("Unexpected error!", e);
234     }
235   }
236 
237   public VersionNode getCurrentVersionNode() { return curentVersion_ ;}
238 
239   public Node getVersion(String versionName) throws RepositoryException {
240     Node currentNode = getCurrentNode();
241     if ((StringUtils.isBlank(versionName) && StringUtils.isBlank(getCurrentVersionNode().getName()))
242         || (StringUtils.isNotBlank(versionName) && StringUtils.isNotBlank(getCurrentVersionNode().getName())
243             && getCurrentVersionNode().getName().equals(versionName))) {
244       return currentNode;
245     }
246     for (VersionNode versionNode : listVersion) {
247       if(versionNode.getName().equals(versionName)) {
248         return currentNode.getVersionHistory().getVersion(versionName);
249       }
250     }
251     return null;
252   }
253 
254   public Node getCurrentNode() {
255     return NodeLocation.getNodeByLocation(node_);
256   }
257 
258   public void setCurrentNode(Node node) {
259     node_ = NodeLocation.getNodeLocationByNode(node);
260   }
261 
262   public List<VersionNode> getListVersion() {
263     return listVersion;
264   }
265 
266   public void setListVersion(List<VersionNode> listVersion) {
267     this.listVersion = listVersion;
268   }
269 
270   public String getLinkInDocumentsApp(String nodePath) throws Exception {
271     DocumentService documentService = WCMCoreUtils.getService(DocumentService.class);
272     return documentService.getLinkInDocumentsApp(nodePath);
273   }
274 
275   public void setRootVersionNum(String rootVersionNum) {
276     this.rootVersionNum_ = rootVersionNum;
277   }
278 
279   public String getRootVersionNum() {
280     return rootVersionNum_;
281   }
282 
283   private boolean isWebContent() throws Exception {
284     Node currentNode = getCurrentNode();
285     if (currentNode != null) {
286       return currentNode.isNodeType(Utils.EXO_WEBCONTENT);
287     }
288     return false;
289   }
290 
291   static  public class RestoreVersionActionListener extends EventListener<UIVersionInfo> {
292     public void execute(Event<UIVersionInfo> event) throws Exception {
293       UIVersionInfo uiVersionInfo = event.getSource();
294       UIJCRExplorer uiExplorer = uiVersionInfo.getAncestorOfType(UIJCRExplorer.class) ;
295       PDFViewerService pdfViewerService = WCMCoreUtils.getService(PDFViewerService.class);
296       CacheService caService = WCMCoreUtils.getService(CacheService.class);
297       ExoCache<Serializable, Object> pdfCache;
298       if(pdfViewerService != null){
299         pdfCache = pdfViewerService.getCache();
300       }else{
301         pdfCache = caService.getCacheInstance(CACHE_NAME);
302       }
303       for(UIComponent uiChild : uiVersionInfo.getChildren()) {
304         uiChild.setRendered(false) ;
305       }
306       String objectId = event.getRequestContext().getRequestParameter(OBJECTID) ;
307       VersionNode currentVersionNode = uiVersionInfo.rootVersion_.findVersionNode(objectId);
308       String fromVersionName  = currentVersionNode.getName() ;
309       UIApplication uiApp = uiVersionInfo.getAncestorOfType(UIApplication.class) ;
310       Node currentNode = uiVersionInfo.getCurrentNode();
311       uiExplorer.addLockToken(currentNode);
312       try {
313         if(!currentNode.isCheckedOut()) {
314           currentNode.checkout();
315         }
316         AutoVersionService autoVersionService = WCMCoreUtils.getService(AutoVersionService.class);
317         Version addedVersion = autoVersionService.autoVersion(currentNode);
318         currentNode.restore(fromVersionName,true);
319         if(!currentNode.isCheckedOut()) {
320           currentNode.checkout();
321         }
322         StringBuilder bd = new StringBuilder();
323         bd.append(((ManageableRepository)currentNode.getSession().getRepository()).getConfiguration().getName()).
324                 append("/").append(currentNode.getSession().getWorkspace().getName()).append("/").
325                 append(currentNode.getUUID());
326         StringBuilder bd1 = new StringBuilder().append(bd).append("/jcr:lastModified");
327         StringBuilder bd2 = new StringBuilder().append(bd).append("/jcr:baseVersion");
328         pdfCache.remove(new ObjectKey(bd.toString()));
329         pdfCache.remove(new ObjectKey(bd1.toString()));
330         pdfCache.remove(new ObjectKey(bd2.toString()));
331 
332         int lastVersionIndice = Integer.parseInt(addedVersion.getName());
333 
334         String restoredFromMsg = "UIDiff.label.restoredFrom_" + fromVersionName + "_" + (lastVersionIndice + 1);
335         VersionHistory versionHistory = currentNode.getVersionHistory();
336         versionHistory.addVersionLabel(versionHistory.getRootVersion().getName(), restoredFromMsg, false);
337 
338         ListenerService listenerService = WCMCoreUtils.getService(ListenerService.class);
339         ActivityCommonService activityService = WCMCoreUtils.getService(ActivityCommonService.class);
340         try {
341           if (listenerService!=null && activityService !=null && activityService.isAcceptedNode(currentNode)) {
342             listenerService.broadcast(ActivityCommonService.NODE_REVISION_CHANGED, currentNode, fromVersionName);
343           }
344         }catch (Exception e) {
345           if (LOG.isErrorEnabled()) {
346             LOG.error("Can not notify NodeMovedActivity: " + e.getMessage());
347           }
348         }
349       } catch(JCRInvalidItemStateException invalid) {
350         uiApp.addMessage(new ApplicationMessage("UIVersionInfo.msg.invalid-item-state", null,
351             ApplicationMessage.WARNING)) ;
352 
353         return ;
354       } catch(NullPointerException nuException){
355         uiApp.addMessage(new ApplicationMessage("UIVersionInfo.msg.invalid-item-state", null,
356             ApplicationMessage.WARNING)) ;
357 
358         return;
359       } catch(Exception e) {
360         //JCRExceptionManager.process(uiApp, e);
361         uiApp.addMessage(new ApplicationMessage("UIVersionInfo.msg.invalid-item-state", null,
362             ApplicationMessage.WARNING)) ;
363 
364         return;
365       }
366       uiVersionInfo.activate();
367       event.getRequestContext().addUIComponentToUpdateByAjax(uiVersionInfo) ;
368       uiExplorer.setIsHidePopup(true) ;
369     }
370   }
371 
372   static public class DeleteVersionActionListener extends EventListener<UIVersionInfo> {
373     public void execute(Event<UIVersionInfo> event) throws Exception {
374       UIVersionInfo uiVersionInfo = event.getSource();
375       UIJCRExplorer uiExplorer = uiVersionInfo.getAncestorOfType(UIJCRExplorer.class);
376       for (UIComponent uiChild : uiVersionInfo.getChildren()) {
377         uiChild.setRendered(false);
378       }
379       String objectId = event.getRequestContext().getRequestParameter(OBJECTID);
380       uiVersionInfo.curentVersion_ = uiVersionInfo.getRootVersionNode().findVersionNode(objectId);
381       Node node = uiVersionInfo.getCurrentNode();
382       UIApplication app = uiVersionInfo.getAncestorOfType(UIApplication.class);
383       try {
384         node.getSession().save();
385         node.getSession().refresh(false);
386         VersionHistoryUtils.removeVersion(uiVersionInfo.getCurrentNode(), uiVersionInfo.curentVersion_.getName() );
387         uiVersionInfo.rootVersion_ = new VersionNode(node, uiExplorer.getSession());
388         uiVersionInfo.curentVersion_ = uiVersionInfo.rootVersion_;
389         if (!node.isCheckedOut())
390           node.checkout();
391         uiExplorer.getSession().save();
392         uiVersionInfo.activate();
393         event.getRequestContext().addUIComponentToUpdateByAjax(uiVersionInfo);
394       } catch (ReferentialIntegrityException rie) {
395         if (LOG.isErrorEnabled()) {
396           LOG.error("Unexpected error", rie);
397         }
398         app.addMessage(new ApplicationMessage("UIVersionInfo.msg.error-removing-referenced-version", null, ApplicationMessage.ERROR));
399         return;
400       } catch (Exception e) {
401         if (LOG.isErrorEnabled()) {
402           LOG.error("Unexpected error", e);
403         }
404         app.addMessage(new ApplicationMessage("UIVersionInfo.msg.error-removing-version", null, ApplicationMessage.ERROR));
405         return;
406       }
407     }
408   }
409 
410   static  public class CompareVersionActionListener extends EventListener<UIVersionInfo> {
411     public void execute(Event<UIVersionInfo> event) throws Exception {
412       UIVersionInfo uiVersionInfo = event.getSource();
413       UIDocumentWorkspace uiDocumentWorkspace = uiVersionInfo.getAncestorOfType(UIDocumentWorkspace.class);
414       for (UIComponent uiChild : uiDocumentWorkspace.getChildren()) {
415         uiChild.setRendered(false);
416       }
417       String fromVersionName = event.getRequestContext().getRequestParameter("versions").split(",")[0];
418       String toVersionName = event.getRequestContext().getRequestParameter("versions").split(",")[1];
419       UIDiff uiDiff = uiDocumentWorkspace.getChild(UIDiff.class);
420       uiDiff.setVersions(uiVersionInfo.getVersion(fromVersionName), uiVersionInfo.getVersion(toVersionName));
421       uiDiff.setRendered(true);
422       event.getRequestContext().addUIComponentToUpdateByAjax(uiDocumentWorkspace);
423     }
424   }
425 
426   static public class SelectActionListener extends EventListener<UIVersionInfo> {
427     public void execute(Event<UIVersionInfo> event) throws Exception {
428       UIVersionInfo uiVersionInfo = event.getSource() ;
429       String path = event.getRequestContext().getRequestParameter(OBJECTID) ;
430       VersionNode root = uiVersionInfo.getRootVersionNode() ;
431       VersionNode selectedVersion= root.findVersionNode(path);
432       selectedVersion.setExpanded(!selectedVersion.isExpanded()) ;
433       event.getRequestContext().addUIComponentToUpdateByAjax(uiVersionInfo) ;
434     }
435   }
436 
437   static public class CloseActionListener extends EventListener<UIVersionInfo> {
438     public void execute(Event<UIVersionInfo> event) throws Exception {
439       UIVersionInfo uiVersionInfo = event.getSource();
440       for(UIComponent uiChild : uiVersionInfo.getChildren()) {
441         if (uiChild.isRendered()) {
442           uiChild.setRendered(false);
443           return ;
444         }
445       }
446       UIJCRExplorer uiExplorer = uiVersionInfo.getAncestorOfType(UIJCRExplorer.class) ;
447       uiExplorer.updateAjax(event) ;
448     }
449   }
450 
451   public static class AddSummaryActionListener extends EventListener<UIVersionInfo> {
452     public void execute(Event<UIVersionInfo> event) throws Exception {
453       UIVersionInfo uiVersionInfo = event.getSource();
454       String objectId = event.getRequestContext().getRequestParameter(OBJECTID) ;
455       uiVersionInfo.curentVersion_  = uiVersionInfo.rootVersion_.findVersionNode(objectId) ;
456       String currentVersionName = uiVersionInfo.curentVersion_.getName();
457       if(StringUtils.isBlank(currentVersionName)) {
458         currentVersionName = uiVersionInfo.getRootVersionNum();
459       }
460       String summary = event.getRequestContext().getRequestParameter("value") + "_" + currentVersionName;
461       UIJCRExplorer uiExplorer = uiVersionInfo.getAncestorOfType(UIJCRExplorer.class) ;
462       UIApplication uiApp = uiVersionInfo.getAncestorOfType(UIApplication.class) ;
463       Node currentNode = uiExplorer.getCurrentNode() ;
464       if(!Utils.isNameValid(summary, Utils.SPECIALCHARACTER)) {
465         uiApp.addMessage(new ApplicationMessage("UILabelForm.msg.label-invalid",
466             null, ApplicationMessage.WARNING)) ;
467         return ;
468       }
469       try{
470         if(StringUtils.isNotBlank(summary) && !currentNode.getVersionHistory().hasVersionLabel(summary)) {
471           Version currentVersion = null;
472           if(currentVersionName.equals(uiVersionInfo.getRootVersionNum())) {
473             currentVersion = currentNode.getVersionHistory().getRootVersion();
474           } else {
475             currentVersion = currentNode.getVersionHistory().getVersion(currentVersionName);
476           }
477           String[] versionLabels = currentNode.getVersionHistory().getVersionLabels(currentVersion);
478           for(String label : versionLabels) {
479             currentNode.getVersionHistory().removeVersionLabel(label);
480           }
481           currentNode.getVersionHistory().addVersionLabel(currentVersion.getName(), summary, false);
482         }
483       } catch (VersionException ve) {
484         uiApp.addMessage(new ApplicationMessage("UILabelForm.msg.label-exist", new Object[]{summary})) ;
485         return ;
486       }
487       event.getRequestContext().addUIComponentToUpdateByAjax(uiVersionInfo) ;
488     }
489   }
490 }