1 package org.argeo.connect.ui.util;
2
3 import java.text.DateFormat;
4 import java.text.SimpleDateFormat;
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.jcr.Item;
9 import javax.jcr.Node;
10 import javax.jcr.NodeIterator;
11 import javax.jcr.Property;
12 import javax.jcr.PropertyIterator;
13 import javax.jcr.PropertyType;
14 import javax.jcr.RepositoryException;
15 import javax.jcr.Value;
16
17 import org.argeo.cms.auth.CurrentUser;
18 import org.argeo.cms.ui.eclipse.forms.AbstractFormPart;
19 import org.argeo.cms.ui.eclipse.forms.FormToolkit;
20 import org.argeo.cms.ui.eclipse.forms.IManagedForm;
21 import org.argeo.cms.util.CmsUtils;
22 import org.argeo.connect.ConnectConstants;
23 import org.argeo.connect.ConnectException;
24 import org.argeo.connect.ConnectNames;
25 import org.argeo.connect.UserAdminService;
26 import org.argeo.connect.util.ConnectJcrUtils;
27 import org.argeo.connect.versioning.ItemDiff;
28 import org.argeo.connect.versioning.VersionDiff;
29 import org.argeo.connect.versioning.VersionUtils;
30 import org.argeo.eclipse.ui.EclipseUiUtils;
31 import org.argeo.jcr.PropertyDiff;
32 import org.argeo.node.NodeConstants;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.layout.FillLayout;
35 import org.eclipse.swt.layout.GridData;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Label;
38 import org.eclipse.swt.widgets.Text;
39
40
41
42
43
44 public class HistoryLog extends LazyCTabControl {
45 private static final long serialVersionUID = -4736848221960630767L;
46
47
48 public final static String CTAB_ID = "org.argeo.connect.ui.ctab.history";
49
50 private final IManagedForm editor;
51 private final FormToolkit toolkit;
52 private final UserAdminService userAdminService;
53 private final Node entity;
54 private DateFormat dateTimeFormat = new SimpleDateFormat(ConnectConstants.DEFAULT_DATE_TIME_FORMAT);
55
56
57 private MyFormPart myFormPart;
58
59 public HistoryLog(Composite parent, int style, FormToolkit toolkit, IManagedForm editor,
60 UserAdminService userAdminService, Node entity) {
61 super(parent, style);
62 this.editor = editor;
63 this.toolkit = toolkit;
64 this.userAdminService = userAdminService;
65 this.entity = entity;
66 }
67
68 @Override
69 public void refreshPartControl() {
70 myFormPart.refresh();
71 layout(true, true);
72 }
73
74 @Override
75 public void createPartControl(Composite parent) {
76 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
77
78
79 if (CurrentUser.isInRole(NodeConstants.ROLE_DATA_ADMIN) || CurrentUser.isInRole(NodeConstants.ROLE_ADMIN)) {
80 Label label = new Label(parent, SWT.WRAP);
81 CmsUtils.markup(label);
82 GridData gd = EclipseUiUtils.fillWidth();
83 gd.verticalIndent = 3;
84 gd.horizontalIndent = 5;
85 label.setLayoutData(gd);
86 StringBuilder builder = new StringBuilder();
87 String puid = ConnectJcrUtils.get(entity, ConnectNames.CONNECT_UID);
88 if (EclipseUiUtils.notEmpty(puid)) {
89 builder.append("Connect UID: ").append(puid);
90 builder.append(" <br/>");
91 }
92 builder.append("JcrID: ").append(ConnectJcrUtils.getIdentifier(entity));
93 builder.append(" <br/>");
94
95 builder.append("Path: ").append(ConnectJcrUtils.getPath(entity));
96 label.setText(builder.toString());
97 }
98 Composite historyCmp = new Composite(parent, SWT.NONE);
99 historyCmp.setLayoutData(EclipseUiUtils.fillAll());
100 historyCmp.setLayout(new FillLayout());
101 final Text styledText = toolkit.createText(historyCmp, "", SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL);
102 myFormPart = new MyFormPart(styledText);
103 myFormPart.initialize(editor);
104 editor.addPart(myFormPart);
105 }
106
107 private class MyFormPart extends AbstractFormPart {
108 private final Text text;
109
110 public MyFormPart(Text text) {
111 this.text = text;
112 }
113
114 @Override
115 public void refresh() {
116 super.refresh();
117 refreshHistory(text);
118 }
119 }
120
121 protected void refreshHistory(Text styledText) {
122 try {
123 List<VersionDiff> lst = VersionUtils.listHistoryDiff(entity, VersionUtils.DEFAULT_FILTERED_OUT_PROP_NAMES);
124 StringBuilder main = new StringBuilder();
125
126 for (int i = lst.size() - 1; i >= 0; i--) {
127 StringBuilder firstL = new StringBuilder();
128 if (i == 0)
129 firstL.append("Creation (");
130 else
131 firstL.append("Update " + i + " (");
132
133 if (lst.get(i).getUserId() != null)
134 firstL.append("User : " + userAdminService.getUserDisplayName(lst.get(i).getUserId()) + ", ");
135 if (lst.get(i).getUpdateTime() != null) {
136 firstL.append("Date : ");
137 firstL.append(dateTimeFormat.format(lst.get(i).getUpdateTime().getTime()));
138 }
139 firstL.append(")");
140 String fl = firstL.toString();
141 main.append(fl).append("\n");
142 for (int j = 0; j < fl.length(); j++)
143 main.append("=");
144 main.append("\n");
145
146 StringBuilder buf = new StringBuilder();
147 Map<String, ItemDiff> diffs = lst.get(i).getDiffs();
148 for (String prop : diffs.keySet()) {
149 ItemDiff diff = diffs.get(prop);
150 Item refItem = diff.getReferenceItem();
151 Item newItem = diff.getObservedItem();
152 Item tmpItem = refItem == null ? newItem : refItem;
153 if (tmpItem instanceof Property)
154 if (((Property) tmpItem).isMultiple())
155 appendMultiplePropertyModif(buf, (Property) newItem, (Property) refItem);
156 else {
157 String refValueStr = "";
158 String newValueStr = "";
159 if (refItem != null)
160 refValueStr = getValueAsString(((Property) refItem).getValue());
161 if (newItem != null)
162 newValueStr = getValueAsString(((Property) newItem).getValue());
163 appendPropModif(buf, diff.getType(), propLabel(diff.getRelPath()), refValueStr,
164 newValueStr);
165 }
166 else {
167 String refStr = refItem == null ? null : ((Node) refItem).getName();
168 String obsStr = newItem == null ? null : ((Node) newItem).getName();
169 appendNodeModif(buf, (Node) newItem, diff.getType(), diff.getRelPath(), refStr, obsStr);
170 }
171 }
172 buf.append("\n");
173 main.append(buf);
174 }
175 styledText.setText(main.toString());
176 } catch (RepositoryException e) {
177 throw new ConnectException("Cannot generate history for current entity.", e);
178 }
179 }
180
181 private String getValueAsString(Value refValue) throws RepositoryException {
182 String refValueStr;
183 if (refValue.getType() == PropertyType.DATE) {
184 refValueStr = dateTimeFormat.format(refValue.getDate().getTime());
185 }
186
187 else
188 refValueStr = refValue.getString();
189
190 return refValueStr;
191 }
192
193 private void appendPropModif(StringBuilder buf, Integer type, String label, String oldValue, String newValue) {
194
195 if (type == ItemDiff.MODIFIED) {
196 buf.append("\t");
197 buf.append(label).append(": ");
198 buf.append(oldValue);
199 buf.append(" > ");
200 buf.append(newValue);
201 buf.append("\n");
202 } else if (type == PropertyDiff.ADDED && !"".equals(newValue)) {
203
204
205 buf.append("\t");
206 buf.append(label).append(": ");
207 buf.append(" + ");
208 buf.append(newValue);
209 buf.append("\n");
210 } else if (type == PropertyDiff.REMOVED) {
211 buf.append("\t");
212 buf.append(label).append(": ");
213 buf.append(" - ");
214 buf.append(oldValue);
215 buf.append("\n");
216 }
217 }
218
219 private void appendNodeModif(StringBuilder buf, Node node, Integer type, String label, String oldValue,
220 String newValue) throws RepositoryException {
221 if (type == PropertyDiff.MODIFIED) {
222 buf.append("Node ");
223 buf.append(label).append(" modified: ");
224 buf.append("\n");
225 } else if (type == PropertyDiff.ADDED) {
226 buf.append("Node ");
227 buf.append(label).append(" added: ");
228 buf.append("\n");
229 appendAddedNodeProperties(buf, node, 1);
230 } else if (type == PropertyDiff.REMOVED) {
231 buf.append("Node ");
232 buf.append(label).append(" removed: ");
233 buf.append("\n");
234 }
235 }
236
237
238 private void appendAddedNodeProperties(StringBuilder builder, Node node, int level) throws RepositoryException {
239 PropertyIterator pit = node.getProperties();
240 while (pit.hasNext()) {
241 Property prop = pit.nextProperty();
242 if (!VersionUtils.DEFAULT_FILTERED_OUT_PROP_NAMES.contains(prop.getName())) {
243 String label = propLabel(prop.getName());
244 for (int i = 0; i < level; i++)
245 builder.append("\t");
246 builder.append(label).append(": ");
247 builder.append(" + ");
248 if (prop.isMultiple())
249 builder.append(ConnectJcrUtils.getMultiAsString(prop.getParent(), prop.getName(), "; "));
250 else
251 builder.append(getValueAsString(prop.getValue()));
252 builder.append("\n");
253 }
254 }
255 NodeIterator nit = node.getNodes();
256 while (nit.hasNext()) {
257 Node currNode = nit.nextNode();
258 for (int i = 0; i < level; i++)
259 builder.append("\t");
260 builder.append("Sub Node ");
261 builder.append(currNode.getName()).append(" added: ");
262 builder.append("\n");
263 appendAddedNodeProperties(builder, currNode, level + 1);
264 }
265 }
266
267 private void appendMultiplePropertyModif(StringBuilder builder, Property obsProp, Property refProp)
268 throws RepositoryException {
269
270 Value[] refValues = null;
271 if (refProp != null)
272 refValues = refProp.getValues();
273
274 Value[] newValues = null;
275 if (obsProp != null)
276 newValues = obsProp.getValues();
277 if (refProp != null)
278 refValues: for (Value refValue : refValues) {
279 if (obsProp != null)
280 for (Value newValue : newValues) {
281 if (refValue.equals(newValue))
282 continue refValues;
283 }
284 appendPropModif(builder, PropertyDiff.REMOVED, propLabel(refProp.getName()), getValueAsString(refValue),
285 null);
286 }
287 if (obsProp != null)
288 newValues: for (Value newValue : newValues) {
289 if (refProp != null)
290 for (Value refValue : refValues) {
291 if (refValue.equals(newValue))
292 continue newValues;
293 }
294 appendPropModif(builder, PropertyDiff.ADDED, propLabel(obsProp.getName()), null,
295 getValueAsString(newValue));
296 }
297 }
298
299
300 protected String propLabel(String str) {
301 if (str.lastIndexOf(":") < 2)
302 return str;
303 else
304 str = str.substring(str.lastIndexOf(":") + 1);
305
306 StringBuilder builder = new StringBuilder();
307
308 for (int i = 0; i < str.length(); i++) {
309 char curr = str.charAt(i);
310 if (i == 0)
311 builder.append(Character.toUpperCase(curr));
312 else if (Character.isUpperCase(curr))
313 builder.append(" ").append(curr);
314 else
315 builder.append(curr);
316 }
317
318 return builder.toString();
319 }
320 }