View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
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
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
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.
15   */
16  package org.argeo.cms.ui.workbench.internal.jcr.parts;
17  
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;
23  import java.util.Map;
24  
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;
35  
36  import org.argeo.cms.ui.CmsConstants;
37  import org.argeo.cms.ui.jcr.FullVersioningTreeContentProvider;
38  import org.argeo.cms.ui.jcr.JcrDClickListener;
39  import org.argeo.cms.ui.jcr.VersionLabelProvider;
40  import org.argeo.cms.ui.workbench.WorkbenchUiPlugin;
41  import org.argeo.cms.ui.workbench.internal.WorkbenchConstants;
42  import org.argeo.eclipse.ui.EclipseUiException;
43  import org.argeo.jcr.JcrUtils;
44  import org.argeo.jcr.PropertyDiff;
45  import org.argeo.jcr.VersionDiff;
46  import org.eclipse.jface.viewers.ITreeContentProvider;
47  import org.eclipse.jface.viewers.TreeViewer;
48  import org.eclipse.swt.SWT;
49  import org.eclipse.swt.layout.GridData;
50  import org.eclipse.swt.layout.GridLayout;
51  import org.eclipse.swt.widgets.Composite;
52  import org.eclipse.swt.widgets.Text;
53  import org.eclipse.ui.forms.AbstractFormPart;
54  import org.eclipse.ui.forms.IManagedForm;
55  import org.eclipse.ui.forms.editor.FormEditor;
56  import org.eclipse.ui.forms.editor.FormPage;
57  import org.eclipse.ui.forms.widgets.FormToolkit;
58  import org.eclipse.ui.forms.widgets.ScrolledForm;
59  import org.eclipse.ui.forms.widgets.Section;
60  import org.eclipse.ui.forms.widgets.TableWrapData;
61  import org.eclipse.ui.forms.widgets.TableWrapLayout;
62  
63  /**
64   * Offers two main sections : one to display a text area with a summary of all
65   * variations between a version and its predecessor and one tree view that
66   * enable browsing
67   */
68  public class NodeVersionHistoryPage extends FormPage implements WorkbenchConstants {
69  	// private final static Log log = LogFactory
70  	// .getLog(NodeVersionHistoryPage.class);
71  
72  	// Utils
73  	protected DateFormat timeFormatter = new SimpleDateFormat(CmsConstants.DATE_TIME_FORMAT);
74  
75  	// business objects
76  	private Node currentNode;
77  
78  	// this page UI components
79  	private FullVersioningTreeContentProvider nodeContentProvider;
80  	private TreeViewer nodesViewer;
81  	private FormToolkit tk;
82  
83  	public NodeVersionHistoryPage(FormEditor editor, String title, Node currentNode) {
84  		super(editor, "NodeVersionHistoryPage", title);
85  		this.currentNode = currentNode;
86  	}
87  
88  	protected void createFormContent(IManagedForm managedForm) {
89  		ScrolledForm form = managedForm.getForm();
90  		form.setText(WorkbenchUiPlugin.getMessage("nodeVersionHistoryPageTitle"));
91  		tk = managedForm.getToolkit();
92  		Composite innerBox = form.getBody();
93  		// Composite innerBox = new Composite(body, SWT.NO_FOCUS);
94  		GridLayout twt = new GridLayout(1, false);
95  		twt.marginWidth = twt.marginHeight = 5;
96  		innerBox.setLayout(twt);
97  		try {
98  			if (!currentNode.isNodeType(NodeType.MIX_VERSIONABLE)) {
99  				tk.createLabel(innerBox, WorkbenchUiPlugin.getMessage("warningUnversionableNode"));
100 			} else {
101 				createHistorySection(innerBox);
102 				createTreeSection(innerBox);
103 			}
104 		} catch (RepositoryException e) {
105 			throw new EclipseUiException("Unable to check if node is versionable", e);
106 		}
107 	}
108 
109 	protected void createTreeSection(Composite parent) {
110 		Section section = tk.createSection(parent, Section.TWISTIE);
111 		section.setLayoutData(new GridData(GridData.FILL_BOTH));
112 		section.setText(WorkbenchUiPlugin.getMessage("versionTreeSectionTitle"));
113 
114 		Composite body = tk.createComposite(section, SWT.FILL);
115 		section.setClient(body);
116 		section.setExpanded(true);
117 		body.setLayoutData(new GridData(GridData.FILL_BOTH));
118 		body.setLayout(new GridLayout());
119 
120 		nodeContentProvider = new FullVersioningTreeContentProvider();
121 		nodesViewer = createNodeViewer(body, nodeContentProvider);
122 		nodesViewer.setInput(currentNode);
123 	}
124 
125 	protected TreeViewer createNodeViewer(Composite parent, final ITreeContentProvider nodeContentProvider) {
126 
127 		final TreeViewer tmpNodeViewer = new TreeViewer(parent, SWT.MULTI);
128 
129 		tmpNodeViewer.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
130 
131 		tmpNodeViewer.setContentProvider(nodeContentProvider);
132 		tmpNodeViewer.setLabelProvider(new VersionLabelProvider());
133 		tmpNodeViewer.addDoubleClickListener(new JcrDClickListener(tmpNodeViewer));
134 		return tmpNodeViewer;
135 	}
136 
137 	protected void createHistorySection(Composite parent) {
138 
139 		// Section Layout
140 		Section section = tk.createSection(parent, Section.TWISTIE);
141 		section.setLayoutData(new GridData(TableWrapData.FILL_GRAB));
142 		TableWrapLayout twt = new TableWrapLayout();
143 		section.setLayout(twt);
144 
145 		// Set title of the section
146 		section.setText(WorkbenchUiPlugin.getMessage("versionHistorySectionTitle"));
147 
148 		final Text styledText = tk.createText(section, "",
149 				SWT.FULL_SELECTION | SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
150 		section.setClient(styledText);
151 		styledText.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.FILL_GRAB));
152 		refreshHistory(styledText);
153 		styledText.setEditable(false);
154 		section.setExpanded(false);
155 
156 		AbstractFormPart part = new AbstractFormPart() {
157 			public void commit(boolean onSave) {
158 			}
159 
160 			public void refresh() {
161 				super.refresh();
162 				refreshHistory(styledText);
163 			}
164 		};
165 		getManagedForm().addPart(part);
166 	}
167 
168 	protected void refreshHistory(Text styledText) {
169 		try {
170 			List<VersionDiff> lst = listHistoryDiff();
171 			StringBuffer main = new StringBuffer("");
172 
173 			for (int i = lst.size() - 1; i >= 0; i--) {
174 				if (i == 0)
175 					main.append("Creation (");
176 				else
177 					main.append("Update " + i + " (");
178 
179 				if (lst.get(i).getUserId() != null)
180 					main.append("UserId : " + lst.get(i).getUserId());
181 
182 				if (lst.get(i).getUserId() != null && lst.get(i).getUpdateTime() != null)
183 					main.append(", ");
184 
185 				if (lst.get(i).getUpdateTime() != null)
186 					main.append("Date : " + timeFormatter.format(lst.get(i).getUpdateTime().getTime()) + ")\n");
187 
188 				StringBuffer buf = new StringBuffer("");
189 				Map<String, PropertyDiff> diffs = lst.get(i).getDiffs();
190 				for (String prop : diffs.keySet()) {
191 					PropertyDiff pd = diffs.get(prop);
192 					// String propName = pd.getRelPath();
193 					Value refValue = pd.getReferenceValue();
194 					Value newValue = pd.getNewValue();
195 					String refValueStr = "";
196 					String newValueStr = "";
197 
198 					if (refValue != null) {
199 						if (refValue.getType() == PropertyType.DATE) {
200 							refValueStr = timeFormatter.format(refValue.getDate().getTime());
201 						} else
202 							refValueStr = refValue.getString();
203 					}
204 					if (newValue != null) {
205 						if (newValue.getType() == PropertyType.DATE) {
206 							newValueStr = timeFormatter.format(newValue.getDate().getTime());
207 						} else
208 							newValueStr = newValue.getString();
209 					}
210 
211 					if (pd.getType() == PropertyDiff.MODIFIED) {
212 						buf.append(prop).append(": ");
213 						buf.append(refValueStr);
214 						buf.append(" > ");
215 						buf.append(newValueStr);
216 						buf.append("\n");
217 					} else if (pd.getType() == PropertyDiff.ADDED && !"".equals(newValueStr)) {
218 						// we don't list property that have been added with an
219 						// empty string as value
220 						buf.append(prop).append(": ");
221 						buf.append(" + ");
222 						buf.append(newValueStr);
223 						buf.append("\n");
224 					} else if (pd.getType() == PropertyDiff.REMOVED) {
225 						buf.append(prop).append(": ");
226 						buf.append(" - ");
227 						buf.append(refValueStr);
228 						buf.append("\n");
229 					}
230 				}
231 				buf.append("\n");
232 				main.append(buf);
233 			}
234 			styledText.setText(main.toString());
235 		} catch (RepositoryException e) {
236 			throw new EclipseUiException("Cannot generate history for node", e);
237 		}
238 	}
239 
240 	public List<VersionDiff> listHistoryDiff() {
241 		try {
242 			List<VersionDiff> res = new ArrayList<VersionDiff>();
243 			VersionManager versionManager = currentNode.getSession().getWorkspace().getVersionManager();
244 			VersionHistory versionHistory = versionManager.getVersionHistory(currentNode.getPath());
245 
246 			VersionIterator vit = versionHistory.getAllLinearVersions();
247 			while (vit.hasNext()) {
248 				Version version = vit.nextVersion();
249 				Node node = version.getFrozenNode();
250 				Version predecessor = null;
251 				try {
252 					predecessor = version.getLinearPredecessor();
253 				} catch (Exception e) {
254 					// no predecessor seems to throw an exception even if it
255 					// shouldn't...
256 				}
257 				if (predecessor == null) {// original
258 				} else {
259 					Map<String, PropertyDiff> diffs = JcrUtils.diffProperties(predecessor.getFrozenNode(), node);
260 					if (!diffs.isEmpty()) {
261 						String lastUserName = null;
262 						Calendar lastUpdate = null;
263 						try {
264 							if (currentNode.isNodeType(NodeType.MIX_LAST_MODIFIED)) {
265 								lastUserName = node.getProperty(Property.JCR_LAST_MODIFIED_BY).getString();
266 								lastUpdate = node.getProperty(Property.JCR_LAST_MODIFIED).getDate();
267 							} else
268 								lastUpdate = version.getProperty(Property.JCR_CREATED).getDate();
269 
270 						} catch (Exception e) {
271 							// Silent that info is optional
272 						}
273 						VersionDiff vd = new VersionDiff(lastUserName, lastUpdate, diffs);
274 						res.add(vd);
275 					}
276 				}
277 			}
278 			return res;
279 		} catch (RepositoryException e) {
280 			throw new EclipseUiException("Cannot generate history for node ");
281 		}
282 
283 	}
284 
285 	@Override
286 	public void setActive(boolean active) {
287 		super.setActive(active);
288 	}
289 }