Introduction
Dialog definition has changed between M4.5.x and M5. This is mainly due to the changes of the UI component as we moved from HTML forms to Vaadin UI for dialogs and form.
The main changes are:
- Configuration of action implementation and definition
- 4.5 allows you to define
saveHandler
on dialog definition. - 5.x replaces this with a direct action definition on the dialog.
- 4.5 allows you to define
- Structure of a dialog, tab and field definitions
- 5.x introduced a tab layer in dialogs.
- Replacement of controls by fields
M5 Dialog definition | M4.5 Dialog definition |
---|---|
stkTextImage | stkTextImage |
actions | |
commit | |
class | |
label | |
cancel | |
form | |
tabs | tabText |
tabText | subtitle |
fields | text |
subtitle | |
text | |
label | |
i18nBasename |
|
label |
|
Preparation steps
In order to migrate dialogs, some preparation has to be performed in the following areas:
- Custom save handler replacement.
- Custom controls replacement.
First identify all your custom components. TODO: Are more detailed steps needed?
Save handler replacement
Replace custom save handlers with either a custom action or a custom property transformer.
Custom action
Replace your save handler with a custom action if the save handler:
- Acts on more than just form properties.
- Defines complex logic.
In this case you will have to create a custom action and definition. Let's take a basic example:
Create a Definition class
public class CustomSaveActionDefinition extends SaveDialogActionDefinition { public CustomSaveActionDefinition() { // This allows you to directly register your action setImplementationClass(CustomSaveAction.class); } }
Create an Action class
public class CustomSaveAction extends SaveDialogAction { public CustomSaveAction(CustomSaveActionDefinition definition, Item item, EditorValidator validator, EditorCallback callback) { super(definition, item, validator, callback); } @Override public void execute() throws ActionExecutionException { // First Validate validator.showValidation(true); if (validator.isValid()) { // Get the JcrNodeAdapter that contains all field properties final JcrNodeAdapter itemChanged = (JcrNodeAdapter) item; try { String fieldPropertyText = itemChanged.getItemProperty("fieldPropertyText").toString(); // Manipulate the data, and do your business logic ... // Once this is done, set the transformed value to the itemChanged if you want that they are persisted on the related JCR node // The related node will be updated based on the item value by the following call: final Node node = itemChanged.applyChanges(); node.getSession().save();
Register the custom save action to the migration task
Create a specific ActionCreator
In case you need to configure more than a class and label on your custom action definition, you will need to create a custom ActionCreator
.
public class CustomActionCreator implements ActionCreator { private final String name; private final String label; private final String myCustomProperty; private final String className; public BaseActionCreator(String name, String label, String className, String myCustomProperty) { this.className = className; this.label = label; this.name = name; this.myCustomProperty = myCustomProperty; } @Override public void create(Node actionsNode) throws RepositoryException { // Create the action node Node actionNode = actionsNode.addNode(this.name, NodeTypes.ContentNode.NAME); // Create the properties actionNode.setProperty("label", this.label); actionNode.setProperty("class", this.className); .... }
From your custom dialog migration task
You can create a custom dialog migration class extending DialogMigrationTask
. Extending this class let you define the custom controls and action to use during dialog migration.
/** * Field Migration task for the specific Form fields. */ public class CustomDialogMigrationTask extends DialogMigrationTask { public CustomDialogMigrationTask(String moduleName) { super(moduleName); } @Override protected void registerActionsForDialogToCreate(HashMap<String, List<ActionCreator>> dialogActionsToMigrate) { // Register the Custom Save Action ActionCreator saveAction = new CustomActionCreator("commit", "save changes", CustomSaveAction.class.getName(), "myProperty"); // Register a standard Cancel Action ActionCreator cancelAction = new BaseActionCreator("cancel", "cancel", "info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition"); // Create an entry dialogActionsToMigrate.put("dialogName", Arrays.asList(saveAction, cancelAction)); }
In this case, during migration when dialogs named "dialogName" are handled, the specific CustomSaveAction
is set as save action.
You may also simply create a new instance of DialogMigrationTask
in your version handler and pass the HashMap<String, List<ActionCreator>> dialogActionsToMigrate
in the constructor parameter.
Custom Transformer
Custom controls replacement
Magnolia 5 introduced some new fields (previously called controls), so the first step is to check if your custom control is available as a standard M5 field (List of fields). If this is not the case, you will need to create and register your own field.
Replace a custom control with an existing M5 field
Register a custom ControlMigration to the migration task.
Assume that your control's ID is dateControl
and that the M5 date field is exactly what you were expecting. No additional control migration step is needed. This means that the steps performed in DateControlMigration are sufficient.
What the task does:
- Removes the
controlType
property - Create a
class
property pointing toDateFieldDefinition.class.getName()
In this case, just register a new entry into getCustomMigrationTask()
from your custom CustomDialogMigrationTask
public class CustomDialogMigrationTask extends DialogMigrationTask { public CustomDialogMigrationTask(String moduleName) { super(moduleName); } @Override protected void registerControlsToMigrate(HashMap<String, ControlMigrator> controlsToMigrate) { // Define your custom types controlsToMigrate.put("dateControl", new DateControlMigration()); ....
OR (Recommended way if you don't have ActionCreator)
In your version handler inject ControlMigratorsRegistry
, add the migrator to the registry, and then simply call the DialogMigrationTask
@Inject public CustomModuleVersionHandler(ControlMigratorsRegistry controlMigratorsRegistry) { // Register control migration task. controlMigratorsRegistry.register("dateControl", new DateControlMigration()); .... register(DeltaBuilder.update("2.2", "") // migrate dialogs to magnolia 5 dialogs .addTask(new DialogMigrationTask("myCustomModule"))
Additional step needed
Assume that your custom controls uses 2 property split into three M5 DateField.
M4.5.x Control definition | Value | M4.5 Dialog definition | New Value |
---|---|---|---|
dateControl | stkTextImage | ||
dateTime | true | time | true |
dateTimeFormat | yyyy-MM-dd :HH:mm:ss | dateFormat | yyyy-MM-dd |
timeFormat | HH:mm:ss | ||
controlType | Date | class | info.magnolia.ui.form.field.definition.DateFieldDefinition |
required |
| required |
|
label | Date | label | Date |
DateControlMigration
already handles the migration of the controlType
property to class
.
Now a CustomDateControlMigration
has to be written in order to manipulate the dateTime and dateTimeFormat properties.
/** * Migrate an custom dateControl to a DateField. */ public class CustomDateControlMigration implements DateControlMigration { @Override public void migrate(Node controlNode) throws RepositoryException { super.migrate(controlNode); // Remove the first property Property dateTimeProperty = controlNode.getProperty("dateTime"); PropertyUtil.renameProperty(dateTimeProperty, "time"); // Split the second property Property formatProperty = controlNode.getProperty("dateTimeFormat"); controlNode.setProperty("dateFormat",StringUtils.left(formatProperty.getString(), StringUtils.indexOf(formatProperty.getString(), ":"))); controlNode.setProperty("timeFormat",StringUtils.right(formatProperty.getString(), StringUtils.indexOf(formatProperty.getString(), ":"))+1); formatProperty.remove(); } }
Register a new entry into getCustomMigrationTask()
from your custom CustomDialogMigrationTask
public class CustomDialogMigrationTask extends DialogMigrationTask { .... @Override protected void registerControlsToMigrate(HashMap<String, ControlMigrator> controlsToMigrate) { // Define your custom types controlsToMigrate.put("dateControl", new CustomDateControlMigration()); ....
Replace a custom control with a new field
Assume that you have a dialog displaying a list of user specific information and then normal text fields. For the user specific information display the best approach would be to extend the StaticField
field. In M5, Field
are created by FieldFactory
referenced by a FieldDefinition
. In general, if you want to add functionality to an existing Field
, you will only have to override the related FieldFactory
, define a new FieldDefinition
and register the new Field
.
Coming back to our example, the StaticFielFactory
expose a 'public String createFieldValue()'
that allows to create the message to display.
Create your custom FieldFactory
public class StaticCustomFieldFactory extends StaticFieldFactory<StaticCustomFieldDefinition> { private static final Logger log = LoggerFactory.getLogger(StaticCustomFieldFactory.class); // Injected factory private MyFactory factory; @Inject public StaticCustomFieldFactory(StaticCustomFieldDefinition definition, Item relatedFieldItem, MyFactory factory) { super(definition, relatedFieldItem); this.factory = factory; } @Override public String createFieldValue() { String value = ""; try { // put your logic here this.factory.do(... ... } return value; }
Create your custom FieldDefinition
/** * Definition used for create a StaticFormField. */ public class StaticCustomFieldDefinition extends StaticFieldDefinition { }
Register the new custom field
Register a custom ControlMigrator
to the migration task.
Create a CustomControlMigrator
that will migrate your old controls to the newly created field. This task will basically
- create the new field definitions property
- remove the old property like controls,...
public class StaticCustomControlMigrator implements ControlMigrator { @Override public void migrate(Node controlNode) throws RepositoryException { controlNode.getProperty("controlType").remove(); controlNode.setProperty("class", StaticCustomFieldDefinition.class.getName()); ... } }
This task is called during the dialog migration process once registered.
public class CustomDialogMigrationTask extends DialogMigrationTask { .... @Override protected void registerControlsToMigrate(HashMap<String, ControlMigrator> controlsToMigrate) { // Define your custom types controlsToMigrate.put("dateControl", new CustomDateControlMigration()); controlsToMigrate.put("myControl", new StaticCustomControlMigrator()); ....
Dialog migration task
GOAL:
REQUIREMENTS:
STEPS:
Class: DialogMigrationTask
Constructor attributes:
Property | Description | Default value | Valid values |
---|---|---|---|
moduleName | Name of the module where the migration will be performed. Mandatory. | String |
Custom implementation
Based on the previous code example:
public class CustomDialogMigrationTask extends DialogMigrationTask { public CustomDialogMigrationTask(String moduleName) { super(moduleName); } @Override protected void registerActionsForDialogToCreate(HashMap<String, List<ActionCreator>> dialogActionsToMigrate) { // Register the Custom Save Action ActionCreator saveAction = new CustomActionCreator("commit", "save changes", CustomSaveAction.class.getName(), "myProperty"); // Register a standard Cancel Action ActionCreator cancelAction = new BaseActionCreator("cancel", "cancel", "info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition"); // Create an entry dialogActionsToMigrate.put("dialogName", Arrays.asList(saveAction, cancelAction)); } @Override protected void registerControlsToMigrate(HashMap<String, ControlMigrator> controlsToMigrate) { // Define your custom types controlsToMigrate.put("dateControl", new CustomDateControlMigration()); controlsToMigrate.put("myControl", new StaticCustomControlMigrator()); }