2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.cms
.ui
.workbench
.internal
.jcr
.parts
;
18 import java
.text
.DateFormat
;
19 import java
.text
.SimpleDateFormat
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Calendar
;
22 import java
.util
.List
;
25 import javax
.jcr
.Node
;
26 import javax
.jcr
.Property
;
27 import javax
.jcr
.PropertyType
;
28 import javax
.jcr
.RepositoryException
;
29 import javax
.jcr
.Value
;
30 import javax
.jcr
.nodetype
.NodeType
;
31 import javax
.jcr
.version
.Version
;
32 import javax
.jcr
.version
.VersionHistory
;
33 import javax
.jcr
.version
.VersionIterator
;
34 import javax
.jcr
.version
.VersionManager
;
36 import org
.argeo
.cms
.ui
.workbench
.WorkbenchUiPlugin
;
37 import org
.argeo
.cms
.ui
.workbench
.internal
.WorkbenchConstants
;
38 import org
.argeo
.cms
.ui
.workbench
.internal
.jcr
.FullVersioningTreeContentProvider
;
39 import org
.argeo
.cms
.ui
.workbench
.internal
.jcr
.GenericNodeDoubleClickListener
;
40 import org
.argeo
.cms
.ui
.workbench
.internal
.jcr
.VersionLabelProvider
;
41 import org
.argeo
.eclipse
.ui
.EclipseUiException
;
42 import org
.argeo
.jcr
.JcrUtils
;
43 import org
.argeo
.jcr
.PropertyDiff
;
44 import org
.argeo
.jcr
.VersionDiff
;
45 import org
.eclipse
.jface
.viewers
.ITreeContentProvider
;
46 import org
.eclipse
.jface
.viewers
.TreeViewer
;
47 import org
.eclipse
.swt
.SWT
;
48 import org
.eclipse
.swt
.layout
.GridData
;
49 import org
.eclipse
.swt
.layout
.GridLayout
;
50 import org
.eclipse
.swt
.widgets
.Composite
;
51 import org
.eclipse
.swt
.widgets
.Text
;
52 import org
.eclipse
.ui
.forms
.AbstractFormPart
;
53 import org
.eclipse
.ui
.forms
.IManagedForm
;
54 import org
.eclipse
.ui
.forms
.editor
.FormEditor
;
55 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
56 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
57 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
58 import org
.eclipse
.ui
.forms
.widgets
.Section
;
59 import org
.eclipse
.ui
.forms
.widgets
.TableWrapData
;
60 import org
.eclipse
.ui
.forms
.widgets
.TableWrapLayout
;
63 * Offers two main sections : one to display a text area with a summary of all
64 * variations between a version and its predecessor and one tree view that
67 public class NodeVersionHistoryPage
extends FormPage
implements
69 // private final static Log log = LogFactory
70 // .getLog(NodeVersionHistoryPage.class);
73 protected DateFormat timeFormatter
= new SimpleDateFormat(DATE_TIME_FORMAT
);
76 private Node currentNode
;
78 // this page UI components
79 private FullVersioningTreeContentProvider nodeContentProvider
;
80 private TreeViewer nodesViewer
;
81 private FormToolkit tk
;
83 public NodeVersionHistoryPage(FormEditor editor
, String title
,
85 super(editor
, "NodeVersionHistoryPage", title
);
86 this.currentNode
= currentNode
;
89 protected void createFormContent(IManagedForm managedForm
) {
90 ScrolledForm form
= managedForm
.getForm();
91 form
.setText(WorkbenchUiPlugin
.getMessage("nodeVersionHistoryPageTitle"));
92 tk
= managedForm
.getToolkit();
93 GridLayout twt
= new GridLayout(1, false);
94 twt
.marginWidth
= twt
.marginHeight
= 5;
95 Composite body
= form
.getBody();
99 if (!currentNode
.isNodeType(NodeType
.MIX_VERSIONABLE
)) {
101 WorkbenchUiPlugin
.getMessage("warningUnversionableNode"));
103 createHistorySection(form
.getBody());
104 createTreeSection(form
.getBody());
106 } catch (RepositoryException e
) {
107 throw new EclipseUiException(
108 "Unexpected error while checking if node is versionable", e
);
112 protected void createTreeSection(Composite parent
) {
113 // Section Layout & MetaData
114 Section section
= tk
.createSection(parent
, Section
.TWISTIE
);
115 section
.setLayoutData(new GridData(GridData
.FILL_BOTH
));
116 section
.setText(WorkbenchUiPlugin
.getMessage("versionTreeSectionTitle"));
119 Composite body
= tk
.createComposite(section
, SWT
.FILL
);
120 // WARNING : 2 following lines are compulsory or body won't be
122 body
.setLayout(new GridLayout());
123 section
.setClient(body
);
125 body
.setLayoutData(new GridData(GridData
.FILL_BOTH
));
126 section
.setExpanded(true);
128 nodeContentProvider
= new FullVersioningTreeContentProvider();
129 nodesViewer
= createNodeViewer(body
, nodeContentProvider
);
130 nodesViewer
.setInput(currentNode
);
133 protected TreeViewer
createNodeViewer(Composite parent
,
134 final ITreeContentProvider nodeContentProvider
) {
136 final TreeViewer tmpNodeViewer
= new TreeViewer(parent
, SWT
.MULTI
);
138 tmpNodeViewer
.getTree().setLayoutData(
139 new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
141 tmpNodeViewer
.setContentProvider(nodeContentProvider
);
142 tmpNodeViewer
.setLabelProvider(new VersionLabelProvider());
144 .addDoubleClickListener(new GenericNodeDoubleClickListener(
146 return tmpNodeViewer
;
149 protected void createHistorySection(Composite parent
) {
152 Section section
= tk
.createSection(parent
, Section
.TWISTIE
);
153 section
.setLayoutData(new GridData(TableWrapData
.FILL_GRAB
));
154 TableWrapLayout twt
= new TableWrapLayout();
155 section
.setLayout(twt
);
157 // Set title of the section
158 section
.setText(WorkbenchUiPlugin
159 .getMessage("versionHistorySectionTitle"));
161 final Text styledText
= tk
.createText(section
, "", SWT
.FULL_SELECTION
162 | SWT
.BORDER
| SWT
.MULTI
| SWT
.WRAP
| SWT
.V_SCROLL
);
163 styledText
.setLayoutData(new GridData(SWT
.FILL
, SWT
.FILL
, true, true));
164 section
.setClient(styledText
);
165 refreshHistory(styledText
);
166 styledText
.setEditable(false);
167 section
.setExpanded(false);
169 AbstractFormPart part
= new AbstractFormPart() {
170 public void commit(boolean onSave
) {
173 public void refresh() {
175 refreshHistory(styledText
);
178 getManagedForm().addPart(part
);
181 protected void refreshHistory(Text styledText
) {
183 List
<VersionDiff
> lst
= listHistoryDiff();
184 StringBuffer main
= new StringBuffer("");
186 for (int i
= lst
.size() - 1; i
>= 0; i
--) {
188 main
.append("Creation (");
190 main
.append("Update " + i
+ " (");
192 if (lst
.get(i
).getUserId() != null)
193 main
.append("UserId : " + lst
.get(i
).getUserId());
195 if (lst
.get(i
).getUserId() != null
196 && lst
.get(i
).getUpdateTime() != null)
199 if (lst
.get(i
).getUpdateTime() != null)
200 main
.append("Date : "
201 + timeFormatter
.format(lst
.get(i
).getUpdateTime()
202 .getTime()) + ")\n");
204 StringBuffer buf
= new StringBuffer("");
205 Map
<String
, PropertyDiff
> diffs
= lst
.get(i
).getDiffs();
206 for (String prop
: diffs
.keySet()) {
207 PropertyDiff pd
= diffs
.get(prop
);
208 // String propName = pd.getRelPath();
209 Value refValue
= pd
.getReferenceValue();
210 Value newValue
= pd
.getNewValue();
211 String refValueStr
= "";
212 String newValueStr
= "";
214 if (refValue
!= null) {
215 if (refValue
.getType() == PropertyType
.DATE
) {
216 refValueStr
= timeFormatter
.format(refValue
217 .getDate().getTime());
219 refValueStr
= refValue
.getString();
221 if (newValue
!= null) {
222 if (newValue
.getType() == PropertyType
.DATE
) {
223 newValueStr
= timeFormatter
.format(newValue
224 .getDate().getTime());
226 newValueStr
= newValue
.getString();
229 if (pd
.getType() == PropertyDiff
.MODIFIED
) {
230 buf
.append(prop
).append(": ");
231 buf
.append(refValueStr
);
233 buf
.append(newValueStr
);
235 } else if (pd
.getType() == PropertyDiff
.ADDED
236 && !"".equals(newValueStr
)) {
237 // we don't list property that have been added with an
238 // empty string as value
239 buf
.append(prop
).append(": ");
241 buf
.append(newValueStr
);
243 } else if (pd
.getType() == PropertyDiff
.REMOVED
) {
244 buf
.append(prop
).append(": ");
246 buf
.append(refValueStr
);
253 styledText
.setText(main
.toString());
254 } catch (RepositoryException e
) {
255 throw new EclipseUiException("Cannot generate history for node", e
);
260 public List
<VersionDiff
> listHistoryDiff() {
262 List
<VersionDiff
> res
= new ArrayList
<VersionDiff
>();
263 VersionManager versionManager
= currentNode
.getSession()
264 .getWorkspace().getVersionManager();
265 VersionHistory versionHistory
= versionManager
266 .getVersionHistory(currentNode
.getPath());
268 VersionIterator vit
= versionHistory
.getAllLinearVersions();
269 while (vit
.hasNext()) {
270 Version version
= vit
.nextVersion();
271 Node node
= version
.getFrozenNode();
272 Version predecessor
= null;
274 predecessor
= version
.getLinearPredecessor();
275 } catch (Exception e
) {
276 // no predecessor seems to throw an exception even if it
279 if (predecessor
== null) {// original
281 Map
<String
, PropertyDiff
> diffs
= JcrUtils
.diffProperties(
282 predecessor
.getFrozenNode(), node
);
283 if (!diffs
.isEmpty()) {
284 String lastUserName
= null;
285 Calendar lastUpdate
= null;
288 .isNodeType(NodeType
.MIX_LAST_MODIFIED
)) {
289 lastUserName
= node
.getProperty(
290 Property
.JCR_LAST_MODIFIED_BY
)
292 lastUpdate
= node
.getProperty(
293 Property
.JCR_LAST_MODIFIED
).getDate();
295 lastUpdate
= version
.getProperty(
296 Property
.JCR_CREATED
).getDate();
298 } catch (Exception e
) {
299 // Silent that info is optional
301 VersionDiff vd
= new VersionDiff(lastUserName
,
308 } catch (RepositoryException e
) {
309 throw new EclipseUiException("Cannot generate history for node ");
315 public void setActive(boolean active
) {
316 super.setActive(active
);