1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.dialog.setup;
35
36 import info.magnolia.cms.util.QueryUtil;
37 import info.magnolia.jcr.predicate.AbstractPredicate;
38 import info.magnolia.jcr.predicate.NodeTypePredicate;
39 import info.magnolia.jcr.util.NodeTypes;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.jcr.util.NodeVisitor;
42 import info.magnolia.jcr.util.PropertyUtil;
43 import info.magnolia.module.InstallContext;
44 import info.magnolia.module.delta.AbstractTask;
45 import info.magnolia.module.delta.TaskExecutionException;
46 import info.magnolia.objectfactory.Components;
47 import info.magnolia.repository.RepositoryConstants;
48 import info.magnolia.ui.dialog.setup.migration.ActionCreator;
49 import info.magnolia.ui.dialog.setup.migration.BaseActionCreator;
50 import info.magnolia.ui.dialog.setup.migration.ControlMigrator;
51 import info.magnolia.ui.dialog.setup.migration.ControlMigratorsRegistry;
52 import info.magnolia.ui.form.field.definition.StaticFieldDefinition;
53
54 import java.util.Arrays;
55 import java.util.HashMap;
56 import java.util.HashSet;
57 import java.util.Iterator;
58 import java.util.List;
59
60 import javax.jcr.Node;
61 import javax.jcr.NodeIterator;
62 import javax.jcr.Property;
63 import javax.jcr.RepositoryException;
64 import javax.jcr.Session;
65 import javax.jcr.query.Query;
66
67 import org.apache.commons.lang3.StringUtils;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75 public class DialogMigrationTask extends AbstractTask {
76
77 private static final Logger log = LoggerFactory.getLogger(DialogMigrationTask.class);
78 private final String moduleName;
79 private static final String propertyNameExtends = "extends";
80 private static final String propertyNameReference = "reference";
81 private final HashSet<Property> extendsAndReferenceProperty = new HashSet<Property>();
82 private ControlMigratorsRegistry controlMigratorsRegistry;
83
84 private HashMap<String, ControlMigrator> controlsToMigrate;
85 private String defaultDialogActions = "defaultDialogActions";
86 private HashMap<String, List<ActionCreator>> dialogActionsToMigrate;
87 private InstallContext installContext;
88
89 private HashMap<String, ControlMigrator> customControlsToMigrate;
90 private HashMap<String, List<ActionCreator>> customDialogActionsToMigrate;
91
92
93
94
95
96
97
98
99 public DialogMigrationTask(String taskName, String taskDescription, String moduleName, HashMap<String, ControlMigrator> customControlsToMigrate, HashMap<String, List<ActionCreator>> customDialogActionsToMigrate) {
100 super(taskName, taskDescription);
101 this.moduleName = moduleName;
102
103
104
105 this.controlMigratorsRegistry = Components.getComponent(ControlMigratorsRegistry.class);
106 this.customControlsToMigrate = customControlsToMigrate;
107 this.customDialogActionsToMigrate = customDialogActionsToMigrate;
108 }
109
110 public DialogMigrationTask(String taskName, String taskDescription, String moduleName) {
111 this(taskName, taskDescription, moduleName, null, null);
112 }
113
114 public DialogMigrationTask(String moduleName) {
115 this("Dialog Migration for 5.x", "Migrate dialog for the following module: " + moduleName, moduleName, null, null);
116 }
117
118
119
120
121 @Override
122 public void execute(InstallContext installContext) throws TaskExecutionException {
123 Session session = null;
124 this.installContext = installContext;
125 try {
126 registerControlsAndActionsMigrators();
127
128 String dialogNodeName = "dialogs";
129 String dialogPath = "/modules/" + moduleName + "/" + dialogNodeName;
130 session = installContext.getJCRSession(RepositoryConstants.CONFIG);
131
132
133 if (!session.itemExists(dialogPath)) {
134 log.warn("Dialog definition do not exist for the following module {}. No Dialog migration task will be performed", moduleName);
135 return;
136 }
137 Node dialog = session.getNode(dialogPath);
138
139 resolveRelativeExtendsPath(dialog);
140 NodeUtil.visit(dialog, new NodeVisitor() {
141 @Override
142 public void visit(Node current) throws RepositoryException {
143 for (Node dialogNode : NodeUtil.getNodes(current, NodeTypes.ContentNode.NAME)) {
144 performDialogMigration(dialogNode);
145 }
146 }
147 }, new NodeTypePredicate(NodeTypes.Content.NAME));
148
149
150 postProcessForExtendsAndReference();
151
152 } catch (Exception e) {
153 log.error("", e);
154 installContext.warn("Could not Migrate Dialog for the following module " + moduleName);
155 throw new TaskExecutionException("Could not Migrate Dialog ", e);
156 }
157 }
158
159 private void registerControlsAndActionsMigrators() {
160
161 registerControlsToMigrate(customControlsToMigrate);
162 registerDialogActionToCreate(customDialogActionsToMigrate);
163
164 addCustomControlsToMigrate(controlsToMigrate);
165 addCustomDialogActionToCreate(dialogActionsToMigrate);
166 }
167
168
169
170
171 private void registerControlsToMigrate(HashMap<String, ControlMigrator> customControlsToMigrate) {
172 this.controlsToMigrate = new HashMap<String, ControlMigrator>();
173
174 this.controlsToMigrate.putAll(controlMigratorsRegistry.getAllMigrators());
175
176 if (customControlsToMigrate != null) {
177 this.controlsToMigrate.putAll(customControlsToMigrate);
178 }
179 }
180
181
182
183
184
185
186
187
188
189 protected void addCustomControlsToMigrate(HashMap<String, ControlMigrator> controlsToMigrate) {
190 }
191
192
193
194
195 private void registerDialogActionToCreate(HashMap<String, List<ActionCreator>> customDialogActionsToMigrate) {
196 this.dialogActionsToMigrate = new HashMap<String, List<ActionCreator>>();
197
198
199 ActionCreator saveAction = new BaseActionCreator("commit", "save changes", "info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition");
200
201 ActionCreator cancelAction = new BaseActionCreator("cancel", "cancel", "info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition");
202
203 this.dialogActionsToMigrate.put(this.defaultDialogActions, Arrays.asList(saveAction, cancelAction));
204
205 if (customDialogActionsToMigrate != null) {
206 this.dialogActionsToMigrate.putAll(customDialogActionsToMigrate);
207 }
208 }
209
210
211
212
213
214
215
216
217 protected void addCustomDialogActionToCreate(HashMap<String, List<ActionCreator>> dialogActionsToMigrate) {
218 }
219
220
221
222
223 private void performDialogMigration(Node dialog) throws RepositoryException {
224
225 Iterable<Node> tabNodes = NodeUtil.getNodes(dialog, DIALOG_FILTER);
226 if (tabNodes.iterator().hasNext()) {
227
228 if (dialog.hasProperty("controlType") && dialog.getProperty("controlType").getString().equals("tab")) {
229 handleTab(dialog);
230 } else {
231
232 if (!dialog.hasProperty("controlType") && !dialog.hasProperty(propertyNameExtends) && !dialog.hasProperty(propertyNameReference)) {
233 handleAction(dialog);
234 }
235
236 handleTabs(dialog, tabNodes.iterator());
237 }
238
239 if (dialog.hasProperty("class")) {
240 dialog.getProperty("class").remove();
241 }
242 } else {
243
244 handleField(dialog);
245 }
246
247 handleExtendsAndReference(dialog);
248 }
249
250
251
252
253 private void handleAction(Node dialog) throws RepositoryException {
254
255 dialog.addNode("actions", NodeTypes.ContentNode.NAME);
256 Node actionsNode = dialog.getNode("actions");
257
258 List<ActionCreator> actions = dialogActionsToMigrate.get(defaultDialogActions);
259
260 if (dialogActionsToMigrate.containsKey(dialog.getName())) {
261 actions = dialogActionsToMigrate.get(dialog.getName());
262 }
263
264 for (ActionCreator action : actions) {
265 action.create(actionsNode);
266 }
267
268 }
269
270
271
272
273 private void handleTabs(Node dialog, Iterator<Node> tabNodes) throws RepositoryException {
274 Node form = dialog.addNode("form", NodeTypes.ContentNode.NAME);
275 handleFormLabels(dialog, form);
276 Node dialogTabs = form.addNode("tabs", NodeTypes.ContentNode.NAME);
277 while (tabNodes.hasNext()) {
278 Node tab = tabNodes.next();
279
280 handleTab(tab);
281
282 NodeUtil.moveNode(tab, dialogTabs);
283 }
284 }
285
286
287
288
289 private void handleFormLabels(Node dialog, Node form) throws RepositoryException {
290 moveAndRenameLabelProperty(dialog, form, "label");
291 moveAndRenameLabelProperty(dialog, form, "i18nBasename");
292 moveAndRenameLabelProperty(dialog, form, "description");
293 }
294
295
296
297
298 private void moveAndRenameLabelProperty(Node source, Node target, String propertyName) throws RepositoryException {
299 if (source.hasProperty(propertyName)) {
300 Property dialogProperty = source.getProperty(propertyName);
301 target.setProperty(propertyName, dialogProperty.getString());
302 dialogProperty.remove();
303 }
304 }
305
306
307
308
309 private void handleTab(Node tab) throws RepositoryException {
310 if ((tab.hasProperty("controlType") && StringUtils.equals(tab.getProperty("controlType").getString(), "tab")) || (tab.getParent().hasProperty(propertyNameExtends))) {
311 if (tab.hasProperty("controlType") && StringUtils.equals(tab.getProperty("controlType").getString(), "tab")) {
312
313 tab.getProperty("controlType").remove();
314 }
315
316 Iterator<Node> controls = NodeUtil.getNodes(tab, NodeTypes.ContentNode.NAME).iterator();
317
318 Node fields = tab.addNode("fields", NodeTypes.ContentNode.NAME);
319
320 while (controls.hasNext()) {
321 Node control = controls.next();
322
323 handleField(control);
324
325 NodeUtil.moveNode(control, fields);
326 }
327 } else if (tab.hasNode("inheritable")) {
328
329 Node inheritable = tab.getNode("inheritable");
330 handleExtendsAndReference(inheritable);
331 } else {
332 handleExtendsAndReference(tab);
333 }
334 }
335
336
337
338
339
340 private void handleField(Node fieldNode) throws RepositoryException {
341 if (fieldNode.hasProperty("controlType")) {
342 String controlTypeName = fieldNode.getProperty("controlType").getString();
343
344 if (controlsToMigrate.containsKey(controlTypeName)) {
345 ControlMigrator controlMigration = controlsToMigrate.get(controlTypeName);
346 controlMigration.migrate(fieldNode, installContext);
347 } else {
348 fieldNode.setProperty("class", StaticFieldDefinition.class.getName());
349 if (!fieldNode.hasProperty("value")) {
350 fieldNode.setProperty("value", "Field not yet supported");
351 }
352 log.warn("No field defined for control '{}' for node '{}'", controlTypeName, fieldNode.getPath());
353 }
354 } else {
355
356 handleExtendsAndReference(fieldNode);
357 }
358 }
359
360
361 private void handleExtendsAndReference(Node node) throws RepositoryException {
362 if (node.hasProperty("extends")) {
363
364 extendsAndReferenceProperty.add(node.getProperty(propertyNameExtends));
365 } else if (node.hasProperty("reference")) {
366
367 extendsAndReferenceProperty.add(node.getProperty(propertyNameReference));
368 }
369 }
370
371
372
373
374 private static AbstractPredicate<Node> DIALOG_FILTER = new AbstractPredicate<Node>() {
375 @Override
376 public boolean evaluateTyped(Node node) {
377 try {
378 return !node.getName().startsWith(NodeTypes.JCR_PREFIX)
379 && !NodeUtil.isNodeType(node, NodeTypes.MetaData.NAME) &&
380 NodeUtil.isNodeType(node, NodeTypes.ContentNode.NAME);
381 } catch (RepositoryException e) {
382 return false;
383 }
384 }
385 };
386
387
388
389
390
391 private void postProcessForExtendsAndReference() throws RepositoryException {
392 for (Property p : extendsAndReferenceProperty) {
393 String path = p.getString();
394 if (path.equals("override")) {
395 continue;
396 }
397 if (!isAbsoulutePath(p, path)) {
398 log.warn("Reference from propertyName '{}' to '{}' is an relative path and could not be linked. The initial value will be keeped", p.getPath(), path);
399 continue;
400 }
401
402 if (!p.getSession().nodeExists(path)) {
403
404 String newPath = insertBeforeLastSlashAndTest(p.getSession(), path, "/tabs", "/fields", "/tabs/fields", "/form/tabs");
405 if (newPath != null) {
406 p.setValue(newPath);
407 continue;
408 }
409
410
411 String beging = path.substring(0, path.lastIndexOf("/"));
412 String end = path.substring(beging.lastIndexOf("/"));
413 beging = beging.substring(0, beging.lastIndexOf("/"));
414 newPath = beging + "/form/tabs" + end;
415 if (p.getSession().nodeExists(newPath)) {
416 p.setValue(newPath);
417 continue;
418 }
419
420 newPath = insertBeforeLastSlash(newPath, "/fields");
421 if (p.getSession().nodeExists(newPath)) {
422 p.setValue(newPath);
423 } else {
424 log.warn("Reference from propertyName '{}' to '{}' not found. The initial value will be keeped", p.getPath(), newPath);
425 }
426 }
427 }
428 }
429
430
431
432
433 private boolean isAbsoulutePath(Property p, String path) {
434 try {
435 p.getSession().nodeExists(path);
436 return true;
437 } catch (RepositoryException e) {
438 return false;
439 }
440 }
441
442
443
444
445
446 private String insertBeforeLastSlashAndTest(Session session, String reference, String... toInserts) throws RepositoryException {
447 String res = null;
448 for (String toInsert : toInserts) {
449 String newPath = insertBeforeLastSlash(reference, toInsert);
450 if (session.nodeExists(newPath)) {
451 return newPath;
452 }
453 }
454 return res;
455 }
456
457
458
459
460 private String insertBeforeLastSlash(String reference, String toInsert) {
461 String beging = reference.substring(0, reference.lastIndexOf("/"));
462 String end = reference.substring(reference.lastIndexOf("/"));
463 return beging + toInsert + end;
464 }
465
466
467
468
469
470
471
472 private void resolveRelativeExtendsPath(Node dialog) {
473 try {
474 final String queryString = "SELECT * FROM [nt:base] AS t WHERE ISDESCENDANTNODE(t, '" + dialog.getPath() + "') AND (" + propertyNameExtends + " is not null OR " + propertyNameReference + " is not null)";
475 NodeIterator iterator = QueryUtil.search(RepositoryConstants.CONFIG, queryString, Query.JCR_SQL2);
476 while (iterator.hasNext()) {
477 Node node = iterator.nextNode();
478 String propertyValue = PropertyUtil.getString(node, propertyNameExtends);
479 Property property = null;
480
481 if (StringUtils.isNotBlank(propertyValue)) {
482 property = node.getProperty(propertyNameExtends);
483 } else {
484 propertyValue = PropertyUtil.getString(node, propertyNameReference);
485 property = node.getProperty(propertyNameReference);
486 }
487
488 if (StringUtils.isNotBlank(propertyValue) && !isAbsoulutePath(property, propertyValue) && node.hasNode(propertyValue)) {
489 property.setValue(node.getNode(propertyValue).getPath());
490 log.info("Change propertyValue of '{}' from '{}' to '{}'", property.getPath(), propertyValue, node.getNode(propertyValue).getPath());
491 }
492 }
493 } catch (RepositoryException e) {
494 log.warn("Could not handle extends/reference property for the following definition ", NodeUtil.getNodePathIfPossible(dialog));
495 }
496 }
497
498 }