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