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.text.DateFormat;
20  import java.util.Calendar;
21  import java.util.Locale;
22  
23  import javax.jcr.Node;
24  import javax.jcr.PathNotFoundException;
25  import javax.jcr.Property;
26  import javax.jcr.RepositoryException;
27  import javax.jcr.version.Version;
28  import javax.jcr.version.VersionHistory;
29  
30  import org.apache.commons.lang.StringUtils;
31  
32  import org.exoplatform.commons.diff.DiffResult;
33  import org.exoplatform.commons.diff.DiffService;
34  import org.exoplatform.ecm.webui.component.explorer.UIDocumentWorkspace;
35  import org.exoplatform.ecm.webui.utils.Utils;
36  import org.exoplatform.portal.webui.util.Util;
37  import org.exoplatform.services.document.DocumentReader;
38  import org.exoplatform.services.document.DocumentReaderService;
39  import org.exoplatform.services.document.HandlerNotFoundException;
40  import org.exoplatform.services.log.ExoLogger;
41  import org.exoplatform.services.log.Log;
42  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
43  import org.exoplatform.webui.config.annotation.ComponentConfig;
44  import org.exoplatform.webui.config.annotation.EventConfig;
45  import org.exoplatform.webui.core.UIComponent;
46  import org.exoplatform.webui.event.Event;
47  import org.exoplatform.webui.event.EventListener;
48  
49  /**
50   * Created by The eXo Platform SARL Author : Pham Tuan tuan.pham@exoplatform.com
51   * May 3, 2007
52   */
53  
54  @ComponentConfig(template = "app:/groovy/webui/component/explorer/versions/UIDiff.gtmpl", events = {
55      @EventConfig(listeners = UIDiff.CompareActionListener.class),
56      @EventConfig(listeners = UIDiff.CloseCompareActionListener.class) })
57  public class UIDiff extends UIComponent {
58    private static final String EXO_LAST_MODIFIED_DATE = "exo:lastModifiedDate";
59  
60    private static final Log   LOG                 = ExoLogger.getLogger(UIDiff.class.getName());
61  
62    public static final String FROM_PARAM          = "from";
63  
64    public static final String TO_PARAM            = "to";
65  
66    private String             baseVersionName_;
67  
68    private String             baseVersionDate_;
69  
70    private String             baseVersionAuthor_;
71  
72    private String             baseVersionLabel_;
73  
74    private String             versionName_;
75  
76    private String             versionDate_;
77  
78    private String             versionAuthor_;
79  
80    private String             versionLabel_;
81  
82    private String             previousText_       = null;
83  
84    private String             currentText_        = null;
85  
86    private boolean            versionCompareable_ = true;
87  
88    private String             diffResultHTML_     = null;
89  
90    private boolean            isImage_            = false;
91  
92    private String             baseImage_          = null;
93  
94    private String             currentImage_       = null;
95  
96    public void setVersions(Node baseVersion, Node version) throws Exception {
97      UIDocumentWorkspace uiDocumentWorkspace = getAncestorOfType(UIDocumentWorkspace.class);
98      UIVersionInfo uiVersionInfo = uiDocumentWorkspace.getChild(UIVersionInfo.class);
99  
100     versionCompareable_ = baseVersion != null && version != null && !baseVersion.getUUID().equals(version.getUUID());
101     if (!versionCompareable_) {
102       throw new IllegalStateException("Can't compare both versions");
103     } else {
104       Node versionRootVersion = null;
105       if (version instanceof Version) {
106         versionRootVersion = ((Version) version).getContainingHistory().getRootVersion();
107       } else {
108         versionRootVersion = version.getVersionHistory().getRootVersion();
109       }
110 
111       Node baseVersionRootVersion = null;
112       if (baseVersion instanceof Version) {
113         baseVersionRootVersion = ((Version) baseVersion).getContainingHistory().getRootVersion();
114       } else {
115         baseVersionRootVersion = baseVersion.getVersionHistory().getRootVersion();
116       }
117 
118       versionCompareable_ = baseVersionRootVersion.getUUID().endsWith(versionRootVersion.getUUID());
119       if (!versionCompareable_) {
120         throw new IllegalStateException("Versions to compare are the same");
121       }
122     }
123 
124     Node currentNode = uiVersionInfo.getCurrentNode();
125     VersionHistory versionHistory = currentNode.getVersionHistory();
126     Version rootVersion = versionHistory.getRootVersion();
127 
128     // Make baseVersion parameter always the oldest version
129     if (currentNode.getUUID().equals(baseVersion.getUUID())) {
130       // switch version and baseVersion to make sure that baseVersion is the
131       // oldest
132       Node tmpNode = baseVersion;
133       baseVersion = version;
134       version = tmpNode;
135     } else if (currentNode.getUUID().equals(version.getUUID())) {
136       // It's ok, the version' indice > baseVersion indice
137     } else if (baseVersion instanceof Version && version instanceof Version) {
138       // compare versions by indice
139       int baseVersionIndice = Integer.parseInt(baseVersion.getName());
140       int versionNumIndice = Integer.parseInt(version.getName());
141 
142       if (baseVersionIndice == versionNumIndice) {
143         throw new IllegalStateException("Can't compare the same version");
144       } else if (baseVersionIndice > versionNumIndice) {
145         Node tmpNode = baseVersion;
146         baseVersion = version;
147         version = tmpNode;
148       }
149     }
150 
151     // Base version is of type Version all the time
152     baseVersionName_ = ((Version) baseVersion).getName();
153     // Current version can be of type Version or Node (draft node)
154     versionName_ = version instanceof Version ? version.getName() : uiVersionInfo.getRootVersionNum();
155 
156     // Base version is of type Version all the time
157     Calendar modifiedDate = getModifiedDate(baseVersion);
158     baseVersionDate_ = formatDate(modifiedDate);
159 
160     // Current version can be of type Version or Node (draft node)
161     modifiedDate = getModifiedDate(version);
162     versionDate_ = formatDate(modifiedDate);
163 
164     baseVersionAuthor_ = getLastModifier(baseVersion);
165     versionAuthor_ = getLastModifier(version);
166 
167     // Base version is of type Version all the time
168     String[] baseVersionLabels = versionHistory.getVersionLabels((Version) baseVersion);
169     baseVersionLabel_ = baseVersionLabels == null || baseVersionLabels.length == 0 ? null : baseVersionLabels[0];
170     // Current version can be of type Version or Node (draft node)
171     if (version instanceof Version) {
172       String[] versionLabels = versionHistory.getVersionLabels((Version) version);
173       versionLabel_ = versionLabels == null || versionLabels.length == 0 ? null : versionLabels[0];
174     } else {
175       String[] versionLabels = versionHistory.getVersionLabels(rootVersion);
176       versionLabel_ = versionLabels == null || versionLabels.length == 0 ? null : versionLabels[0];
177     }
178 
179     isImage_ = isOriginalNodeImage(version) && isOriginalNodeImage(baseVersion);
180 
181     previousText_ = null;
182     currentText_ = null;
183     diffResultHTML_ = null;
184     currentImage_ = null;
185     baseImage_ = null;
186     isImage_ = isOriginalNodeImage(version) && isOriginalNodeImage(baseVersion);
187 
188     if (isImage_) {
189       String wsName = currentNode.getSession().getWorkspace().getName();
190       String originalNodePath = currentNode.getPath();
191       String basePath = "/" + WCMCoreUtils.getPortalName() + "/" + WCMCoreUtils.getRestContextName() + "/jcr/"
192           + WCMCoreUtils.getRepository().getConfiguration().getName() + "/" + wsName + originalNodePath;
193 
194       long timeInMS = Calendar.getInstance().getTimeInMillis();
195 
196       currentImage_ = basePath + (currentNode.getUUID().equals(version.getUUID()) ? "?" + timeInMS : ("?version=" + version.getName() + "&" + timeInMS));
197       baseImage_ = basePath + (currentNode.getUUID().equals(baseVersion.getUUID()) ? "?" + timeInMS : "?version=" + baseVersion.getName() + "&" + timeInMS);
198     } else {
199       try {
200         previousText_ = getText(baseVersion);
201         currentText_ = getText(version);
202       } catch (HandlerNotFoundException e) {
203         versionCompareable_ = false;
204       }
205 
206       if (versionCompareable_) {
207         if (StringUtils.isBlank(previousText_) && StringUtils.isBlank(currentText_)) {
208           versionCompareable_ = false;
209         } else if ((previousText_ != null) && (currentText_ != null)) {
210           DiffService diffService = WCMCoreUtils.getService(DiffService.class);
211           DiffResult diffResult = diffService.getDifferencesAsHTML(previousText_, currentText_, true);
212           diffResultHTML_ = diffResult.getDiffHTML();
213         }
214       }
215     }
216   }
217 
218   public String getBaseVersionNum() throws Exception {
219     return baseVersionName_;
220   }
221 
222   public String getCurrentVersionNum() throws Exception {
223     return versionName_;
224   }
225 
226   public String getBaseVersionDate() throws Exception {
227     return baseVersionDate_;
228   }
229 
230   public String getCurrentVersionDate() throws Exception {
231     return versionDate_;
232   }
233 
234   public String getBaseVersionAuthor() {
235     return baseVersionAuthor_;
236   }
237 
238   public String getCurrentVersionAuthor() {
239     return versionAuthor_;
240   }
241 
242   public String getBaseVersionLabel() {
243     return baseVersionLabel_;
244   }
245 
246   public String getCurrentVersionLabel() {
247     return versionLabel_;
248   }
249 
250   public String getBaseImage() throws Exception {
251     return baseImage_;
252   }
253 
254   public String getCurrentImage() throws Exception {
255     return currentImage_;
256   }
257 
258   public boolean isCompareable() {
259     return versionCompareable_;
260   }
261 
262   public String getDifferences() throws Exception {
263     return diffResultHTML_;
264   }
265 
266   public boolean isImage() {
267     return isImage_;
268   }
269 
270   private Calendar getModifiedDate(Node node) throws RepositoryException {
271     Property property = getProperty(node, EXO_LAST_MODIFIED_DATE);
272     return property == null ? null : property.getDate();
273   }
274 
275   private String getLastModifier(Node node) throws RepositoryException {
276     Property property = getProperty(node, Utils.EXO_LASTMODIFIER);
277     return property == null ? null : property.getString();
278   }
279 
280   private boolean isOriginalNodeImage(Node node) throws Exception {
281     Property mimeProperty = getProperty(node, Utils.JCR_MIMETYPE);
282     String mimeType = mimeProperty == null ? null : mimeProperty.getString();
283     return StringUtils.isNotBlank(mimeType) && mimeType.startsWith("image");
284   }
285 
286   private String getText(Node node) throws Exception {
287     Property mimeProperty = getProperty(node, Utils.JCR_MIMETYPE);
288     Property dataProperty = getProperty(node, Utils.JCR_DATA);
289     if (mimeProperty != null && dataProperty != null) {
290       String mimeType = mimeProperty.getString();
291       if (mimeType.startsWith("text")) {
292         return dataProperty.getString();
293       }
294       DocumentReaderService readerService = getApplicationComponent(DocumentReaderService.class);
295       DocumentReader documentReader = readerService.getDocumentReader(mimeType);
296       if (documentReader != null) {
297         try {
298           return documentReader.getContentAsText(dataProperty.getStream());
299         } catch (Exception e) {
300           LOG.warn("An error occured while getting text from file " + node.getPath() + " with mimeType " + mimeType
301               + " with document reader = " + documentReader + ". File comparaison will be disabled", e);
302         }
303       }
304     }
305     throw new HandlerNotFoundException();
306   }
307 
308   private Property getProperty(Node node, String propertyName) throws RepositoryException, PathNotFoundException {
309     Property property = null;
310     if (node instanceof Version) {
311       if (node.hasNode(Utils.JCR_FROZEN)) {
312         node = node.getNode(Utils.JCR_FROZEN);
313       } else {
314         return null;
315       }
316     }
317     if (node.hasProperty(propertyName)) {
318       property = node.getProperty(propertyName);
319     } else if (node.hasNode(Utils.JCR_CONTENT)) {
320       Node content = node.getNode(Utils.JCR_CONTENT);
321       if (content.hasProperty(propertyName)) {
322         property = content.getProperty(propertyName);
323       }
324     }
325     return property;
326   }
327 
328   private String formatDate(Calendar calendar) {
329     Locale currentLocale = Util.getPortalRequestContext().getLocale();
330     DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, currentLocale);
331     return dateFormat.format(calendar.getTime());
332   }
333 
334   static public class CloseCompareActionListener extends EventListener<UIDiff> {
335     public void execute(Event<UIDiff> event) throws Exception {
336       UIDiff uiDiff = event.getSource();
337       if (uiDiff.isRendered()) {
338         uiDiff.setRendered(false);
339       }
340       UIDocumentWorkspace uiDocumentWorkspace = uiDiff.getAncestorOfType(UIDocumentWorkspace.class);
341       UIVersionInfo uiVersionInfo = uiDocumentWorkspace.getChild(UIVersionInfo.class);
342       uiVersionInfo.setRendered(true);
343       event.getRequestContext().addUIComponentToUpdateByAjax(uiDocumentWorkspace);
344     }
345   }
346 
347   static public class CompareActionListener extends EventListener<UIDiff> {
348     public void execute(Event<UIDiff> event) throws Exception {
349       UIDiff uiDiff = (UIDiff) event.getSource();
350       String fromVersionName = event.getRequestContext().getRequestParameter(FROM_PARAM);
351       String toVersionName = event.getRequestContext().getRequestParameter(TO_PARAM);
352       UIDocumentWorkspace uiDocumentWorkspace = uiDiff.getAncestorOfType(UIDocumentWorkspace.class);
353       UIVersionInfo uiVersionInfo = uiDocumentWorkspace.getChild(UIVersionInfo.class);
354       uiDiff.setVersions(uiVersionInfo.getVersion(fromVersionName), uiVersionInfo.getVersion(toVersionName));
355     }
356   }
357 }