You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/04/26 17:46:32 UTC

[2/4] syncope git commit: [SYNCOPE-790] Now binding JSON configuration to role which allows to selectively enable user / group / anyObject wizard steps, customize plain / derived / virtual attributes appearance order and even providing custom class

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
index b95983e..dc58bf1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
@@ -20,52 +20,140 @@ package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
 import org.apache.syncope.client.console.rest.GroupRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.common.lib.EntityTOUtils;
+import org.apache.syncope.common.lib.to.AbstractSchemaTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.model.LoadableDetachableModel;
 
-public abstract class AbstractAttrs extends WizardStep {
+public abstract class AbstractAttrs<S extends AbstractSchemaTO> extends WizardStep {
 
     private static final long serialVersionUID = -5387344116983102292L;
 
-    protected final SchemaRestClient schemaRestClient = new SchemaRestClient();
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
 
-    protected final AnyTypeClassRestClient anyTypeClassRestClient = new AnyTypeClassRestClient();
+    private final AnyTypeClassRestClient anyTypeClassRestClient = new AnyTypeClassRestClient();
 
     private final GroupRestClient groupRestClient = new GroupRestClient();
 
-    protected final AnyTO entityTO;
+    protected final AnyTO anyTO;
 
-    public AbstractAttrs(final AnyTO entityTO) {
-        this.entityTO = entityTO;
+    private final List<String> whichAttrs;
+
+    protected final Map<String, S> schemas = new LinkedHashMap<>();
+
+    protected final LoadableDetachableModel<List<AttrTO>> attrTOs;
+
+    public AbstractAttrs(final AnyTO anyTO, final List<String> anyTypeClasses, final List<String> whichAttrs) {
+        super();
+        this.setOutputMarkupId(true);
+
+        this.anyTO = anyTO;
+        this.whichAttrs = whichAttrs;
+
+        this.attrTOs = new LoadableDetachableModel<List<AttrTO>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<AttrTO> load() {
+                setSchemas(CollectionUtils.collect(anyTypeClassRestClient.list(getAllAuxClasses()),
+                        EntityTOUtils.<AnyTypeClassTO>keyTransformer(), new ArrayList<>(anyTypeClasses)));
+                setAttrs();
+                return new ArrayList<>(getAttrsFromAnyTO());
+            }
+        };
+    }
+
+    protected boolean reoderSchemas() {
+        return !whichAttrs.isEmpty();
     }
 
+    protected abstract SchemaType getSchemaType();
+
+    private void setSchemas(final List<String> anyTypeClasses) {
+        List<S> allSchemas = Collections.emptyList();
+        if (!anyTypeClasses.isEmpty()) {
+            allSchemas = schemaRestClient.getSchemas(getSchemaType(), anyTypeClasses.toArray(new String[] {}));
+        }
+
+        schemas.clear();
+
+        if (reoderSchemas()) {
+            // 1. remove attributes not selected for display
+            CollectionUtils.filter(allSchemas, new Predicate<S>() {
+
+                @Override
+                public boolean evaluate(final S schemaTO) {
+                    return whichAttrs.contains(schemaTO.getKey());
+                }
+            });
+
+            // 2. sort remainig attributes according to configuration, e.g. attrLayout
+            final Map<String, Integer> attrLayoutMap = new HashMap<>(whichAttrs.size());
+            for (int i = 0; i < whichAttrs.size(); i++) {
+                attrLayoutMap.put(whichAttrs.get(i), i);
+            }
+            Collections.sort(allSchemas, new Comparator<S>() {
+
+                @Override
+                public int compare(final S schema1, final S schema2) {
+                    int value = 0;
+
+                    if (attrLayoutMap.get(schema1.getKey()) > attrLayoutMap.get(schema2.getKey())) {
+                        value = 1;
+                    } else if (attrLayoutMap.get(schema1.getKey()) < attrLayoutMap.get(schema2.getKey())) {
+                        value = -1;
+                    }
+
+                    return value;
+                }
+            });
+        }
+        for (S schemaTO : allSchemas) {
+            schemas.put(schemaTO.getKey(), schemaTO);
+        }
+    }
+
+    protected abstract void setAttrs();
+
+    protected abstract Set<AttrTO> getAttrsFromAnyTO();
+
     protected Set<String> getAllAuxClasses() {
         final List<MembershipTO> memberships;
         final List<String> dyngroups;
-        if (entityTO instanceof UserTO) {
-            memberships = UserTO.class.cast(entityTO).getMemberships();
-            dyngroups = UserTO.class.cast(entityTO).getDynGroups();
-        } else if (entityTO instanceof AnyObjectTO) {
-            memberships = AnyObjectTO.class.cast(entityTO).getMemberships();
-            dyngroups = AnyObjectTO.class.cast(entityTO).getDynGroups();
+        if (anyTO instanceof UserTO) {
+            memberships = UserTO.class.cast(anyTO).getMemberships();
+            dyngroups = UserTO.class.cast(anyTO).getDynGroups();
+        } else if (anyTO instanceof AnyObjectTO) {
+            memberships = AnyObjectTO.class.cast(anyTO).getMemberships();
+            dyngroups = AnyObjectTO.class.cast(anyTO).getDynGroups();
         } else {
             memberships = Collections.<MembershipTO>emptyList();
             dyngroups = Collections.<String>emptyList();
         }
 
-        final List<GroupTO> groups = new ArrayList<>();
+        List<GroupTO> groups = new ArrayList<>();
         CollectionUtils.collect(memberships, new Transformer<MembershipTO, GroupTO>() {
 
             @Override
@@ -83,7 +171,7 @@ public abstract class AbstractAttrs extends WizardStep {
             }
         }, groups);
 
-        final Set<String> auxClasses = new HashSet<>(entityTO.getAuxClasses());
+        Set<String> auxClasses = new HashSet<>(anyTO.getAuxClasses());
         for (GroupTO groupTO : groups) {
             auxClasses.addAll(groupTO.getAuxClasses());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
index 6198ce3..8a50f86 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
@@ -21,48 +21,43 @@ package org.apache.syncope.client.console.wizards.any;
 import java.io.Serializable;
 
 import java.util.List;
-
+import org.apache.syncope.client.console.layout.AnyObjectForm;
+import org.apache.syncope.client.console.layout.AnyObjectFormLayoutInfo;
 import org.apache.syncope.common.lib.AnyOperations;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.wicket.PageReference;
 
-public class AnyObjectWizardBuilder extends AnyWizardBuilder<AnyObjectTO> implements Serializable {
+public class AnyObjectWizardBuilder extends AnyWizardBuilder<AnyObjectTO> implements AnyObjectForm {
 
     private static final long serialVersionUID = -2480279868319546243L;
 
-    /**
-     * Construct.
-     *
-     * @param anyObjectTO any object TO.
-     * @param anyTypeClasses any type classes
-     * @param pageRef Caller page reference.
-     */
     public AnyObjectWizardBuilder(
             final AnyObjectTO anyObjectTO,
             final List<String> anyTypeClasses,
+            final AnyObjectFormLayoutInfo formLayoutInfo,
             final PageReference pageRef) {
-        super(anyObjectTO, anyTypeClasses, pageRef);
+
+        super(anyObjectTO, anyTypeClasses, formLayoutInfo, pageRef);
     }
 
     @Override
     protected Serializable onApplyInternal(final AnyHandler<AnyObjectTO> modelObject) {
         final AnyObjectTO inner = modelObject.getInnerObject();
 
-        final ProvisioningResult<AnyObjectTO> actual;
-
+        ProvisioningResult<AnyObjectTO> actual;
         if (inner.getKey() == null) {
             actual = anyObjectRestClient.create(AnyObjectTO.class.cast(inner));
         } else {
-            final AnyObjectPatch patch = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
+            AnyObjectPatch patch = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
 
-            // update user just if it is changed
-            if (!patch.isEmpty()) {
-                actual = anyObjectRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
-            } else {
+            // update just if it is changed
+            if (patch.isEmpty()) {
                 actual = new ProvisioningResult<>();
                 actual.setAny(inner);
+            } else {
+                actual = anyObjectRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
index ae5b657..624b3f2 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
@@ -18,13 +18,15 @@
  */
 package org.apache.syncope.client.console.wizards.any;
 
-import java.io.Serializable;
-
 import java.util.Collections;
 import java.util.List;
-
 import org.apache.syncope.client.console.commons.Mode;
 import org.apache.syncope.client.console.commons.status.StatusBean;
+import org.apache.syncope.client.console.layout.AbstractAnyFormLayout;
+import org.apache.syncope.client.console.layout.AnyForm;
+import org.apache.syncope.client.console.layout.AnyObjectFormLayoutInfo;
+import org.apache.syncope.client.console.layout.GroupFormLayoutInfo;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
 import org.apache.syncope.client.console.rest.AnyObjectRestClient;
 import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -34,8 +36,7 @@ import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.wizard.WizardModel;
 import org.apache.wicket.model.util.ListModel;
 
-public abstract class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilder<AnyHandler<T>>
-        implements Serializable {
+public abstract class AnyWizardBuilder<A extends AnyTO> extends AjaxWizardBuilder<AnyHandler<A>> {
 
     private static final long serialVersionUID = -2480279868319546243L;
 
@@ -43,16 +44,25 @@ public abstract class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilde
 
     protected final List<String> anyTypeClasses;
 
+    protected AbstractAnyFormLayout<A, ? extends AnyForm<A>> formLayoutInfo;
+
     /**
      * Construct.
      *
      * @param anyTO any
      * @param anyTypeClasses any type classes
-     * @param pageRef Caller page reference.
+     * @param formLayoutInfo form layout info
+     * @param pageRef caller page reference.
      */
-    public AnyWizardBuilder(final T anyTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+    public AnyWizardBuilder(
+            final A anyTO,
+            final List<String> anyTypeClasses,
+            final AbstractAnyFormLayout<A, ? extends AnyForm<A>> formLayoutInfo,
+            final PageReference pageRef) {
+
         super(new AnyHandler<>(anyTO), pageRef);
         this.anyTypeClasses = anyTypeClasses;
+        this.formLayoutInfo = formLayoutInfo;
     }
 
     /**
@@ -60,54 +70,92 @@ public abstract class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilde
      *
      * @param handler any handler
      * @param anyTypeClasses any type classes
-     * @param pageRef Caller page reference.
+     * @param formLayoutInfo form layout info
+     * @param pageRef caller page reference.
      */
     public AnyWizardBuilder(
-            final AnyHandler<T> handler,
+            final AnyHandler<A> handler,
             final List<String> anyTypeClasses,
+            final AbstractAnyFormLayout<A, ? extends AnyForm<A>> formLayoutInfo,
             final PageReference pageRef) {
+
         super(handler, pageRef);
         this.anyTypeClasses = anyTypeClasses;
+        this.formLayoutInfo = formLayoutInfo;
     }
 
     @Override
-    protected WizardModel buildModelSteps(final AnyHandler<T> modelObject, final WizardModel wizardModel) {
-        final String[] clazzes = anyTypeClasses.toArray(new String[] {});
+    protected WizardModel buildModelSteps(final AnyHandler<A> modelObject, final WizardModel wizardModel) {
         // optional details panel step
         addOptionalDetailsPanel(modelObject, wizardModel);
 
-        if ((this instanceof GroupWizardBuilder) && (modelObject.getInnerObject() instanceof GroupTO)) {
-            wizardModel.add(new Ownership(GroupHandler.class.cast(modelObject), pageRef));
-            wizardModel.add(new DynamicMemberships(GroupHandler.class.cast(modelObject)));
+        if ((this instanceof GroupWizardBuilder)
+                && (modelObject.getInnerObject() instanceof GroupTO)
+                && (formLayoutInfo instanceof GroupFormLayoutInfo)) {
+
+            GroupFormLayoutInfo groupFormLayoutInfo = GroupFormLayoutInfo.class.cast(formLayoutInfo);
+            if (groupFormLayoutInfo.isOwnership()) {
+                wizardModel.add(new Ownership(GroupHandler.class.cast(modelObject), pageRef));
+            }
+            if (groupFormLayoutInfo.isDynamicMemberships()) {
+                wizardModel.add(new DynamicMemberships(GroupHandler.class.cast(modelObject)));
+            }
         }
 
-        wizardModel.add(new AuxClasses(modelObject.getInnerObject(), clazzes));
+        if (formLayoutInfo.isAuxClasses()) {
+            wizardModel.add(new AuxClasses(modelObject.getInnerObject(), anyTypeClasses));
+        }
 
         // attributes panel steps
-        wizardModel.add(new PlainAttrs(modelObject.getInnerObject(), null, Mode.ADMIN, clazzes));
-        wizardModel.add(new DerAttrs(modelObject.getInnerObject(), clazzes));
-        wizardModel.add(new VirAttrs(modelObject.getInnerObject(), clazzes));
+        if (formLayoutInfo.isPlainAttrs()) {
+            wizardModel.add(new PlainAttrs(
+                    modelObject.getInnerObject(),
+                    null,
+                    Mode.ADMIN,
+                    anyTypeClasses,
+                    formLayoutInfo.getWhichPlainAttrs()));
+        }
+        if (formLayoutInfo.isDerAttrs()) {
+            wizardModel.add(new DerAttrs(
+                    modelObject.getInnerObject(), anyTypeClasses, formLayoutInfo.getWhichDerAttrs()));
+        }
+        if (formLayoutInfo.isVirAttrs()) {
+            wizardModel.add(new VirAttrs(
+                    modelObject.getInnerObject(), anyTypeClasses, formLayoutInfo.getWhichVirAttrs()));
+        }
+
+        // role panel step (just available for users)
+        if ((this instanceof UserWizardBuilder)
+                && (modelObject.getInnerObject() instanceof UserTO)
+                && (formLayoutInfo instanceof UserFormLayoutInfo)
+                && UserFormLayoutInfo.class.cast(formLayoutInfo).isRoles()) {
 
-        // role panel step (jst available for users)
-        if ((this instanceof UserWizardBuilder) && (modelObject.getInnerObject() instanceof UserTO)) {
             wizardModel.add(new Roles(UserTO.class.cast(modelObject.getInnerObject())));
         }
 
-        // relationship panel step (jst available for users)
-        if (!(this instanceof GroupWizardBuilder)) {
+        // relationship panel step (available for users and any objects)
+        if (((formLayoutInfo instanceof UserFormLayoutInfo)
+                && UserFormLayoutInfo.class.cast(formLayoutInfo).isRelationships())
+                || ((formLayoutInfo instanceof AnyObjectFormLayoutInfo)
+                && AnyObjectFormLayoutInfo.class.cast(formLayoutInfo).isRelationships())) {
+
             wizardModel.add(new Relationships(modelObject.getInnerObject(), pageRef));
         }
 
         // resource panel step
-        wizardModel.add(new Resources(modelObject.getInnerObject()));
+        if (formLayoutInfo.isResources()) {
+            wizardModel.add(new Resources(modelObject.getInnerObject()));
+        }
+
         return wizardModel;
     }
 
-    protected AnyWizardBuilder<T> addOptionalDetailsPanel(
-            final AnyHandler<T> modelObject, final WizardModel wizardModel) {
+    protected AnyWizardBuilder<A> addOptionalDetailsPanel(
+            final AnyHandler<A> modelObject, final WizardModel wizardModel) {
+
         if (modelObject.getInnerObject().getKey() != null) {
             wizardModel.add(new Details<>(
-                    modelObject, new ListModel<>(Collections.<StatusBean>emptyList()), pageRef, true));
+                    modelObject, new ListModel<>(Collections.<StatusBean>emptyList()), true, pageRef));
         }
         return this;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AuxClasses.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AuxClasses.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AuxClasses.java
index 47e6669..b70a1c0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AuxClasses.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AuxClasses.java
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
@@ -47,7 +46,7 @@ public class AuxClasses extends WizardStep {
 
     private final GroupRestClient groupRestClient = new GroupRestClient();
 
-    public <T extends AnyTO> AuxClasses(final T entityTO, final String... anyTypeClass) {
+    public <T extends AnyTO> AuxClasses(final T entityTO, final List<String> anyTypeClasses) {
         this.setOutputMarkupId(true);
 
         final Fragment fragment;
@@ -98,7 +97,7 @@ public class AuxClasses extends WizardStep {
                     "groups", new ListModel<>(memberships),
                     new AjaxPalettePanel.Builder.Query<MembershipTO>() {
 
-                private static final long serialVersionUID = 1L;
+                private static final long serialVersionUID = -7223078772249308813L;
 
                 @Override
                 public List<MembershipTO> execute(final String filter) {
@@ -142,11 +141,9 @@ public class AuxClasses extends WizardStep {
         }
         add(fragment);
 
-        final List<String> current = Arrays.asList(anyTypeClass);
-
         final List<String> choices = new ArrayList<>();
         for (AnyTypeClassTO aux : new AnyTypeClassRestClient().list()) {
-            if (!current.contains(aux.getKey())) {
+            if (!anyTypeClasses.contains(aux.getKey())) {
                 choices.add(aux.getKey());
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
index b5e8374..c8ccfee 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
@@ -19,17 +19,12 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Transformer;
+import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.DerSchemaTO;
 import org.apache.syncope.common.lib.types.SchemaType;
@@ -37,70 +32,31 @@ import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.MarkupStream;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
-import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
 
-public class DerAttrs extends AbstractAttrs {
+public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
 
     private static final long serialVersionUID = -5387344116983102292L;
 
-    public <T extends AnyTO> DerAttrs(final T entityTO, final String... anyTypeClass) {
-        super(entityTO);
-        setOutputMarkupId(true);
+    public <T extends AnyTO> DerAttrs(
+            final T anyTO,
+            final List<String> anyTypeClasses,
+            final List<String> whichDerAttrs) {
 
-        final LoadableDetachableModel<List<AttrTO>> derAttrTOs = new LoadableDetachableModel<List<AttrTO>>() {
+        super(anyTO, anyTypeClasses, whichDerAttrs);
 
-            private static final long serialVersionUID = 5275935387613157437L;
-
-            @Override
-            protected List<AttrTO> load() {
-                List<String> anyTypeClasses = CollectionUtils.collect(anyTypeClassRestClient.list(getAllAuxClasses()),
-                        EntityTOUtils.<AnyTypeClassTO>keyTransformer(), new ArrayList<>(Arrays.asList(anyTypeClass)));
-
-                List<DerSchemaTO> derSchemas = Collections.emptyList();
-                if (!anyTypeClasses.isEmpty()) {
-                    derSchemas =
-                            schemaRestClient.getSchemas(SchemaType.DERIVED, anyTypeClasses.toArray(new String[] {}));
-                }
-
-                final Map<String, AttrTO> currents = entityTO.getDerAttrMap();
-                entityTO.getDerAttrs().clear();
-
-                // This conversion from set to lis is required by the ListView.
-                // Not performed by using collect parameter because entityTO change is required.
-                return new ArrayList<>(
-                        CollectionUtils.collect(derSchemas, new Transformer<DerSchemaTO, AttrTO>() {
-
-                            @Override
-                            public AttrTO transform(final DerSchemaTO input) {
-                                AttrTO attrTO = currents.get(input.getKey());
-                                if (attrTO == null) {
-                                    attrTO = new AttrTO();
-                                    attrTO.setSchema(input.getKey());
-                                }
-                                return attrTO;
-                            }
-                        }, entityTO.getDerAttrs()));
-            }
-        };
-
-        final WebMarkupContainer attributesContainer = new WebMarkupContainer("derAttrContainer");
-        attributesContainer.setOutputMarkupId(true);
-        add(attributesContainer);
-
-        ListView<AttrTO> attributes = new ListView<AttrTO>("attrs", derAttrTOs) {
+        add(new ListView<AttrTO>("schemas", attrTOs) {
 
             private static final long serialVersionUID = 9101744072914090143L;
 
             @Override
             public void renderHead(final IHeaderResponse response) {
                 super.renderHead(response);
-                if (derAttrTOs.getObject().isEmpty()) {
+                if (attrTOs.getObject().isEmpty()) {
                     response.render(OnDomReadyHeaderItem.forScript(
                             String.format("$('#emptyPlaceholder').append(\"%s\")", getString("attribute.empty.list"))));
                 }
@@ -114,26 +70,53 @@ public class DerAttrs extends AbstractAttrs {
 
             @Override
             protected void populateItem(final ListItem<AttrTO> item) {
-                final AttrTO attrTO = item.getModelObject();
+                AttrTO attrTO = item.getModelObject();
 
-                final IModel<String> model;
-                final List<String> values = attrTO.getValues();
+                IModel<String> model;
+                List<String> values = attrTO.getValues();
                 if (values == null || values.isEmpty()) {
                     model = new ResourceModel("derived.emptyvalue.message", StringUtils.EMPTY);
                 } else {
                     model = new Model<>(values.get(0));
                 }
 
-                final AjaxTextFieldPanel panel = new AjaxTextFieldPanel(
-                        "panel", attrTO.getSchema(), model, false);
-
+                AjaxTextFieldPanel panel = new AjaxTextFieldPanel("panel", attrTO.getSchema(), model, false);
                 panel.setEnabled(false);
                 panel.setRequired(true);
                 panel.setOutputMarkupId(true);
                 item.add(panel);
+            }
+        });
+    }
 
+    @Override
+    protected SchemaType getSchemaType() {
+        return SchemaType.DERIVED;
+    }
+
+    @Override
+    protected Set<AttrTO> getAttrsFromAnyTO() {
+        return anyTO.getDerAttrs();
+    }
+
+    @Override
+    protected void setAttrs() {
+        List<AttrTO> attrs = new ArrayList<>();
+
+        Map<String, AttrTO> attrMap = anyTO.getDerAttrMap();
+
+        for (DerSchemaTO schema : schemas.values()) {
+            AttrTO attrTO = new AttrTO();
+            attrTO.setSchema(schema.getKey());
+            if (attrMap.containsKey(schema.getKey())) {
+                attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
             }
-        };
-        attributesContainer.add(attributes);
+
+            attrs.add(attrTO);
+        }
+
+        anyTO.getDerAttrs().clear();
+        anyTO.getDerAttrs().addAll(attrs);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
index 8d18d45..41a5573 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
@@ -40,8 +40,9 @@ public class Details<T extends AnyTO> extends WizardStep {
     public Details(
             final AnyHandler<T> handler,
             final IModel<List<StatusBean>> statusModel,
-            final PageReference pageRef,
-            final boolean includeStatusPanel) {
+            final boolean includeStatusPanel,
+            final PageReference pageRef) {
+
         this.pageRef = pageRef;
 
         T anyTO = handler.getInnerObject();

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
index 8595b19..610f2b8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
@@ -34,15 +34,15 @@ public class GroupDetails extends Details<GroupTO> {
             final GroupHandler groupHandler,
             final IModel<List<StatusBean>> statusModel,
             final boolean templateMode,
-            final PageReference pageRef,
-            final boolean includeStatusPanel) {
-        super(groupHandler, statusModel, pageRef, includeStatusPanel);
+            final boolean includeStatusPanel,
+            final PageReference pageRef) {
+
+        super(groupHandler, statusModel, includeStatusPanel, pageRef);
 
         final GroupTO groupTO = GroupHandler.class.cast(groupHandler).getInnerObject();
 
         final AjaxTextFieldPanel name = new AjaxTextFieldPanel("name", "name",
                 new PropertyModel<String>(groupTO, "name"), false);
-
         if (!templateMode) {
             name.addRequiredLabel();
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
index e45afa4..e9fe94b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
@@ -22,6 +22,8 @@ import java.io.Serializable;
 import java.util.Collections;
 import java.util.List;
 import org.apache.syncope.client.console.commons.status.StatusBean;
+import org.apache.syncope.client.console.layout.GroupForm;
+import org.apache.syncope.client.console.layout.GroupFormLayoutInfo;
 import org.apache.syncope.client.console.rest.GroupRestClient;
 import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
 import org.apache.syncope.common.lib.AnyOperations;
@@ -32,21 +34,19 @@ import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.wizard.WizardModel;
 import org.apache.wicket.model.util.ListModel;
 
-public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> {
+public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> implements GroupForm {
 
     private static final long serialVersionUID = 5945391813567245081L;
 
     private final GroupRestClient groupRestClient = new GroupRestClient();
 
-    /**
-     * Construct.
-     *
-     * @param groupTO any
-     * @param anyTypeClasses any type classes
-     * @param pageRef Caller page reference.
-     */
-    public GroupWizardBuilder(final GroupTO groupTO, final List<String> anyTypeClasses, final PageReference pageRef) {
-        super(new GroupHandler(groupTO), anyTypeClasses, pageRef);
+    public GroupWizardBuilder(
+            final GroupTO groupTO,
+            final List<String> anyTypeClasses,
+            final GroupFormLayoutInfo formLayoutInfo,
+            final PageReference pageRef) {
+
+        super(new GroupHandler(groupTO), anyTypeClasses, formLayoutInfo, pageRef);
     }
 
     /**
@@ -65,22 +65,21 @@ public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> {
 
     @Override
     protected Serializable onApplyInternal(final AnyHandler<GroupTO> modelObject) {
-        final ProvisioningResult<GroupTO> actual;
-
-        GroupTO toBeProcessed = modelObject instanceof GroupHandler
+        GroupTO inner = modelObject instanceof GroupHandler
                 ? GroupHandler.class.cast(modelObject).fillDynamicConditions()
                 : modelObject.getInnerObject();
 
-        if (toBeProcessed.getKey() == null) {
-            actual = groupRestClient.create(toBeProcessed);
+        ProvisioningResult<GroupTO> actual;
+        if (inner.getKey() == null) {
+            actual = groupRestClient.create(inner);
         } else {
-            final GroupPatch patch = AnyOperations.diff(toBeProcessed, getOriginalItem().getInnerObject(), false);
-            // update user just if it is changed
-            if (!patch.isEmpty()) {
-                actual = groupRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
-            } else {
+            GroupPatch patch = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
+            // update just if it is changed
+            if (patch.isEmpty()) {
                 actual = new ProvisioningResult<>();
-                actual.setAny(toBeProcessed);
+                actual.setAny(inner);
+            } else {
+                actual = groupRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 
@@ -93,8 +92,8 @@ public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> {
         wizardModel.add(new GroupDetails(
                 GroupHandler.class.cast(modelObject),
                 new ListModel<>(Collections.<StatusBean>emptyList()),
-                false, pageRef,
-                modelObject.getInnerObject().getKey() != null));
+                false,
+                modelObject.getInnerObject().getKey() != null, pageRef));
         return this;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
index 6c17cdd..dc2b5c2 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
@@ -19,15 +19,10 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.collections4.CollectionUtils;
+import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.Mode;
 import org.apache.syncope.client.console.commons.SchemaUtils;
@@ -40,10 +35,8 @@ import org.apache.syncope.client.console.wicket.markup.html.form.DateTextFieldPa
 import org.apache.syncope.client.console.wicket.markup.html.form.DateTimeFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -55,46 +48,33 @@ import org.apache.wicket.markup.html.form.IChoiceRenderer;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 
-public class PlainAttrs extends AbstractAttrs {
+public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
 
     private static final long serialVersionUID = 552437609667518888L;
 
     private final Mode mode;
 
-    private Map<String, PlainSchemaTO> schemas = new LinkedHashMap<>();
-
     public <T extends AnyTO> PlainAttrs(
-            final T entityTO, final Form<?> form, final Mode mode, final String... anyTypeClass) {
-        super(entityTO);
-        this.setOutputMarkupId(true);
+            final T anyTO,
+            final Form<?> form,
+            final Mode mode,
+            final List<String> anyTypeClasses,
+            final List<String> whichPlainAttrs) {
 
+        super(anyTO, anyTypeClasses, whichPlainAttrs);
         this.mode = mode;
 
-        final LoadableDetachableModel<List<AttrTO>> plainAttrTOs = new LoadableDetachableModel<List<AttrTO>>() {
-
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected List<AttrTO> load() {
-                setPlainSchemas(CollectionUtils.collect(anyTypeClassRestClient.list(getAllAuxClasses()),
-                        EntityTOUtils.<AnyTypeClassTO>keyTransformer(), new ArrayList<>(Arrays.asList(anyTypeClass))));
-                setAttrs();
-                return new ArrayList<>(entityTO.getPlainAttrs());
-            }
-        };
-
-        add(new ListView<AttrTO>("schemas", plainAttrTOs) {
+        add(new ListView<AttrTO>("schemas", attrTOs) {
 
             private static final long serialVersionUID = 9101744072914090143L;
 
             @Override
             public void renderHead(final IHeaderResponse response) {
                 super.renderHead(response);
-                if (plainAttrTOs.getObject().isEmpty()) {
+                if (attrTOs.getObject().isEmpty()) {
                     response.render(OnDomReadyHeaderItem.forScript(
                             String.format("$('#emptyPlaceholder').append(\"%s\")", getString("attribute.empty.list"))));
                 }
@@ -103,105 +83,82 @@ public class PlainAttrs extends AbstractAttrs {
             @Override
             @SuppressWarnings({ "unchecked", "rawtypes" })
             protected void populateItem(final ListItem<AttrTO> item) {
-                final AttrTO attributeTO = (AttrTO) item.getDefaultModelObject();
-
-                final FieldPanel panel = getFieldPanel(schemas.get(attributeTO.getSchema()));
+                AttrTO attrTO = item.getModelObject();
 
-                if (mode == Mode.TEMPLATE || !schemas.get(attributeTO.getSchema()).isMultivalue()) {
+                FieldPanel panel = getFieldPanel(schemas.get(attrTO.getSchema()));
+                if (mode == Mode.TEMPLATE || !schemas.get(attrTO.getSchema()).isMultivalue()) {
                     item.add(panel);
-                    panel.setNewModel(attributeTO.getValues());
+                    panel.setNewModel(attrTO.getValues());
                 } else {
                     item.add(new MultiFieldPanel.Builder<>(
-                            new PropertyModel<List<String>>(attributeTO, "values")).build(
+                            new PropertyModel<List<String>>(attrTO, "values")).build(
                             "panel",
-                            attributeTO.getSchema(),
+                            attrTO.getSchema(),
                             panel));
                 }
             }
         });
     }
 
-    private void setPlainSchemas(final List<String> anyTypeClasses) {
-        List<PlainSchemaTO> plainSchemas = Collections.emptyList();
-        if (!anyTypeClasses.isEmpty()) {
-            plainSchemas = schemaRestClient.getSchemas(SchemaType.PLAIN, anyTypeClasses.toArray(new String[] {}));
-        }
-
-        schemas.clear();
-
-        // SYNCOPE-790
-        AttrTO attrLayout = null;
-        if (attrLayout != null && mode != Mode.TEMPLATE) {
-            // 1. remove attributes not selected for display
-            schemaRestClient.filter(plainSchemas, attrLayout.getValues(), true);
-            // 2. sort remainig attributes according to configuration, e.g. attrLayout
-            final Map<String, Integer> attrLayoutMap = new HashMap<>(attrLayout.getValues().size());
-            for (int i = 0; i < attrLayout.getValues().size(); i++) {
-                attrLayoutMap.put(attrLayout.getValues().get(i), i);
-            }
-            Collections.sort(plainSchemas, new Comparator<PlainSchemaTO>() {
-
-                @Override
-                public int compare(final PlainSchemaTO schema1, final PlainSchemaTO schema2) {
-                    int value = 0;
+    @Override
+    protected SchemaType getSchemaType() {
+        return SchemaType.PLAIN;
+    }
 
-                    if (attrLayoutMap.get(schema1.getKey()) > attrLayoutMap.get(schema2.getKey())) {
-                        value = 1;
-                    } else if (attrLayoutMap.get(schema1.getKey()) < attrLayoutMap.get(schema2.getKey())) {
-                        value = -1;
-                    }
+    @Override
+    protected boolean reoderSchemas() {
+        return super.reoderSchemas() && mode != Mode.TEMPLATE;
+    }
 
-                    return value;
-                }
-            });
-        }
-        for (PlainSchemaTO schemaTO : plainSchemas) {
-            schemas.put(schemaTO.getKey(), schemaTO);
-        }
+    @Override
+    protected Set<AttrTO> getAttrsFromAnyTO() {
+        return anyTO.getPlainAttrs();
     }
 
-    private void setAttrs() {
-        final List<AttrTO> entityData = new ArrayList<>();
+    @Override
+    protected void setAttrs() {
+        List<AttrTO> attrs = new ArrayList<>();
 
-        final Map<String, AttrTO> attrMap = entityTO.getPlainAttrMap();
+        Map<String, AttrTO> attrMap = anyTO.getPlainAttrMap();
 
         for (PlainSchemaTO schema : schemas.values()) {
-            final AttrTO attributeTO = new AttrTO();
-            attributeTO.setSchema(schema.getKey());
+            AttrTO attrTO = new AttrTO();
+            attrTO.setSchema(schema.getKey());
 
             if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
-                attributeTO.getValues().add("");
+                attrTO.getValues().add("");
 
                 // is important to set readonly only after values setting
-                attributeTO.setReadonly(schema.isReadonly());
+                attrTO.setReadonly(schema.isReadonly());
             } else {
-                attributeTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
+                attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
             }
-            entityData.add(attributeTO);
+            attrs.add(attrTO);
         }
 
-        entityTO.getPlainAttrs().clear();
-        entityTO.getPlainAttrs().addAll(entityData);
+        anyTO.getPlainAttrs().clear();
+        anyTO.getPlainAttrs().addAll(attrs);
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private FieldPanel getFieldPanel(final PlainSchemaTO schemaTO) {
-        final boolean required = mode == Mode.TEMPLATE
+        boolean required = mode == Mode.TEMPLATE
                 ? false
                 : schemaTO.getMandatoryCondition().equalsIgnoreCase("true");
 
-        final boolean readOnly = mode == Mode.TEMPLATE ? false : schemaTO.isReadonly();
+        boolean readOnly = mode == Mode.TEMPLATE ? false : schemaTO.isReadonly();
 
-        final AttrSchemaType type = mode == Mode.TEMPLATE ? AttrSchemaType.String : schemaTO.getType();
+        AttrSchemaType type = mode == Mode.TEMPLATE ? AttrSchemaType.String : schemaTO.getType();
 
-        final FieldPanel panel;
+        FieldPanel panel;
         switch (type) {
             case Boolean:
                 panel = new AjaxCheckBoxPanel("panel", schemaTO.getKey(), new Model<Boolean>(), false);
                 panel.setRequired(required);
                 break;
+
             case Date:
-                final String dataPattern = schemaTO.getConversionPattern() == null
+                String dataPattern = schemaTO.getConversionPattern() == null
                         ? SyncopeConstants.DEFAULT_DATE_PATTERN
                         : schemaTO.getConversionPattern();
 
@@ -216,6 +173,7 @@ public class PlainAttrs extends AbstractAttrs {
                 }
 
                 break;
+
             case Enum:
                 panel = new AjaxDropDownChoicePanel<>("panel", schemaTO.getKey(), new Model<String>(), false);
                 ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(schemaTO));

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
index 594ea44..c80247e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
@@ -38,7 +38,7 @@ public class Roles extends WizardStep {
     public <T extends AnyTO> Roles(final UserTO entityTO) {
         this.setOutputMarkupId(true);
 
-        final ArrayList<String> allRoles = CollectionUtils.collect(new RoleRestClient().getAll(),
+        final ArrayList<String> allRoles = CollectionUtils.collect(new RoleRestClient().list(),
                 EntityTOUtils.<RoleTO>keyTransformer(), new ArrayList<String>());
 
         add(new AjaxPalettePanel.Builder<String>().build("roles",

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
index b6e071d..28a7d91 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
@@ -50,9 +50,11 @@ public class UserDetails extends Details<UserTO> {
             final IModel<List<StatusBean>> statusModel,
             final boolean resetPassword,
             final boolean templateMode,
-            final PageReference pageRef,
-            final boolean includeStatusPanel) {
-        super(handler, statusModel, pageRef, includeStatusPanel);
+            final boolean includeStatusPanel,
+            final boolean showPasswordManagement,
+            final PageReference pageRef) {
+
+        super(handler, statusModel, includeStatusPanel, pageRef);
 
         final UserTO userTO = handler.getInnerObject();
         // ------------------------
@@ -86,7 +88,7 @@ public class UserDetails extends Details<UserTO> {
         }
         ), model) {
 
-            private static final long serialVersionUID = 1L;
+            private static final long serialVersionUID = -2898628183677758699L;
 
             @Override
             protected Component newTitle(final String markupId, final ITab tab, final Accordion.State state) {
@@ -119,6 +121,7 @@ public class UserDetails extends Details<UserTO> {
         };
 
         accordion.setOutputMarkupId(true);
+        accordion.setVisible(showPasswordManagement);
         add(accordion);
         // ------------------------        
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserInformationPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserInformationPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserInformationPanel.java
index 033bf40..192a74e 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserInformationPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserInformationPanel.java
@@ -35,7 +35,7 @@ public class UserInformationPanel extends AnnotatedBeanPanel {
         // ------------------------
         // Change password date
         // ------------------------
-        add(new Label("changePwdDate", new Model<String>(userTO.getChangePwdDate() == null
+        add(new Label("changePwdDate", new Model<>(userTO.getChangePwdDate() == null
                 ? StringUtils.EMPTY
                 : SyncopeConsoleSession.get().getDateFormat().format(userTO.getChangePwdDate()))));
         // ------------------------
@@ -43,7 +43,7 @@ public class UserInformationPanel extends AnnotatedBeanPanel {
         // ------------------------
         // Last login date
         // ------------------------
-        add(new Label("lastLoginDate", new Model<String>(userTO.getLastLoginDate() == null
+        add(new Label("lastLoginDate", new Model<>(userTO.getLastLoginDate() == null
                 ? StringUtils.EMPTY
                 : SyncopeConsoleSession.get().getDateFormat().format(userTO.getLastLoginDate()))));
         // ------------------------
@@ -51,13 +51,13 @@ public class UserInformationPanel extends AnnotatedBeanPanel {
         // ------------------------
         // Failed logins
         // ------------------------
-        add(new Label("failedLogins", new Model<Integer>(userTO.getFailedLogins())));
+        add(new Label("failedLogins", new Model<>(userTO.getFailedLogins())));
         // ------------------------
 
         // ------------------------
         // Token
         // ------------------------
-        add(new Label("token", new Model<String>(userTO.getToken() == null
+        add(new Label("token", new Model<>(userTO.getToken() == null
                 ? StringUtils.EMPTY
                 : userTO.getToken())));
         // ------------------------
@@ -65,7 +65,7 @@ public class UserInformationPanel extends AnnotatedBeanPanel {
         // ------------------------
         // Token expire time
         // ------------------------
-        add(new Label("tokenExpireTime", new Model<String>(userTO.getTokenExpireTime() == null
+        add(new Label("tokenExpireTime", new Model<>(userTO.getTokenExpireTime() == null
                 ? StringUtils.EMPTY
                 : SyncopeConsoleSession.get().getDateFormat().format(userTO.getTokenExpireTime()))));
         // ------------------------

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
index eb1d490..2f8b5f6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
@@ -24,6 +24,8 @@ import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.status.StatusBean;
 import org.apache.syncope.client.console.commons.status.StatusUtils;
+import org.apache.syncope.client.console.layout.UserForm;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
 import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.common.lib.AnyOperations;
 import org.apache.syncope.common.lib.patch.UserPatch;
@@ -34,7 +36,7 @@ import org.apache.wicket.extensions.wizard.WizardModel;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.util.ListModel;
 
-public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
+public class UserWizardBuilder extends AnyWizardBuilder<UserTO> implements UserForm {
 
     private static final long serialVersionUID = 6716803168859873877L;
 
@@ -42,27 +44,21 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
 
     private final IModel<List<StatusBean>> statusModel;
 
-    /**
-     * Construct.
-     *
-     * @param userTO any
-     * @param anyTypeClasses any type classes
-     * @param pageRef Caller page reference.
-     */
     public UserWizardBuilder(
             final UserTO userTO,
             final List<String> anyTypeClasses,
+            final UserFormLayoutInfo formLayoutInfo,
             final PageReference pageRef) {
-        super(userTO, anyTypeClasses, pageRef);
+
+        super(userTO, anyTypeClasses, formLayoutInfo, pageRef);
         statusModel = new ListModel<>(new ArrayList<StatusBean>());
     }
 
     @Override
     protected Serializable onApplyInternal(final AnyHandler<UserTO> modelObject) {
-        final ProvisioningResult<UserTO> actual;
-
-        final UserTO inner = modelObject.getInnerObject();
+        UserTO inner = modelObject.getInnerObject();
 
+        ProvisioningResult<UserTO> actual;
         if (inner.getKey() == null) {
             actual = userRestClient.create(inner, StringUtils.isNotBlank(inner.getPassword()));
         } else {
@@ -71,12 +67,12 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
                 patch.setPassword(StatusUtils.buildPasswordPatch(inner.getPassword(), statusModel.getObject()));
             }
 
-            // update user just if it is changed
-            if (!patch.isEmpty()) {
-                actual = userRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
-            } else {
+            // update just if it is changed
+            if (patch.isEmpty()) {
                 actual = new ProvisioningResult<>();
                 actual.setAny(inner);
+            } else {
+                actual = userRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 
@@ -88,8 +84,10 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
             final AnyHandler<UserTO> modelObject, final WizardModel wizardModel) {
 
         wizardModel.add(new UserDetails(
-                modelObject, statusModel, false, false, pageRef,
-                modelObject.getInnerObject().getKey() != null));
+                modelObject, statusModel, false, false,
+                modelObject.getInnerObject().getKey() != null,
+                UserFormLayoutInfo.class.cast(formLayoutInfo).isPasswordManagement(),
+                pageRef));
         return this;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
index f5dab16..21b7b9a 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
@@ -19,90 +19,42 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Transformer;
+import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.VirSchemaTO;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.wicket.markup.head.IHeaderResponse;
 import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
-import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 
-public class VirAttrs extends AbstractAttrs {
+public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
 
     private static final long serialVersionUID = -7982691107029848579L;
 
-    public <T extends AnyTO> VirAttrs(final T entityTO, final String... anyTypeClass) {
-        super(entityTO);
-        this.setOutputMarkupId(true);
+    public <T extends AnyTO> VirAttrs(
+            final T anyTO,
+            final List<String> anyTypeClasses,
+            final List<String> whichVirAttrs) {
 
-        final LoadableDetachableModel<List<AttrTO>> virAttrTOs = new LoadableDetachableModel<List<AttrTO>>() {
+        super(anyTO, anyTypeClasses, whichVirAttrs);
 
-            private static final long serialVersionUID = 1L;
-
-            @Override
-            protected List<AttrTO> load() {
-                List<String> anyTypeClasses = CollectionUtils.collect(anyTypeClassRestClient.list(getAllAuxClasses()),
-                        EntityTOUtils.<AnyTypeClassTO>keyTransformer(), new ArrayList<>(Arrays.asList(anyTypeClass)));
-
-                List<VirSchemaTO> virSchemas = Collections.emptyList();
-                if (!anyTypeClasses.isEmpty()) {
-                    virSchemas =
-                            schemaRestClient.getSchemas(SchemaType.VIRTUAL, anyTypeClasses.toArray(new String[] {}));
-                }
-
-                final Map<String, AttrTO> currents = entityTO.getVirAttrMap();
-                entityTO.getVirAttrs().clear();
-
-                // This conversion from set to lis is required by the ListView.
-                // Not performed by using collect parameter because entityTO change is required.
-                return new ArrayList<>(CollectionUtils.collect(virSchemas, new Transformer<VirSchemaTO, AttrTO>() {
-
-                    @Override
-                    public AttrTO transform(final VirSchemaTO input) {
-                        AttrTO attrTO = currents.get(input.getKey());
-                        if (attrTO == null) {
-                            attrTO = new AttrTO();
-                            attrTO.setSchema(input.getKey());
-                            attrTO.getValues().add(StringUtils.EMPTY);
-                        } else if (attrTO.getValues().isEmpty()) {
-                            attrTO.getValues().add("");
-                        }
-
-                        attrTO.setReadonly(input.isReadonly());
-                        return attrTO;
-                    }
-                }, entityTO.getVirAttrs()));
-            }
-        };
-
-        final WebMarkupContainer attributesContainer = new WebMarkupContainer("virAttrContainer");
-        attributesContainer.setOutputMarkupId(true);
-        add(attributesContainer);
-
-        ListView<AttrTO> attributes = new ListView<AttrTO>("attrs", virAttrTOs) {
+        add(new ListView<AttrTO>("schemas", attrTOs) {
 
             private static final long serialVersionUID = 9101744072914090143L;
 
             @Override
             public void renderHead(final IHeaderResponse response) {
                 super.renderHead(response);
-                if (virAttrTOs.getObject().isEmpty()) {
+                if (attrTOs.getObject().isEmpty()) {
                     response.render(OnDomReadyHeaderItem.forScript(
                             String.format("$('#emptyPlaceholder').append(\"%s\")", getString("attribute.empty.list"))));
                 }
@@ -115,17 +67,46 @@ public class VirAttrs extends AbstractAttrs {
 
                 attrTO.setReadonly(attrTO.isReadonly());
 
-                final AjaxTextFieldPanel panel = new AjaxTextFieldPanel(
+                AjaxTextFieldPanel panel = new AjaxTextFieldPanel(
                         "panel", attrTO.getSchema(), new Model<String>(), false);
-
                 item.add(new MultiFieldPanel.Builder<>(
                         new PropertyModel<List<String>>(attrTO, "values")).build(
                         "panel",
                         attrTO.getSchema(),
                         panel).setEnabled(!attrTO.isReadonly()));
             }
-        };
+        });
+    }
+
+    @Override
+    protected SchemaType getSchemaType() {
+        return SchemaType.VIRTUAL;
+    }
+
+    @Override
+    protected Set<AttrTO> getAttrsFromAnyTO() {
+        return anyTO.getVirAttrs();
+    }
+
+    @Override
+    protected void setAttrs() {
+        List<AttrTO> attrs = new ArrayList<>();
+
+        Map<String, AttrTO> attrMap = anyTO.getVirAttrMap();
+
+        for (VirSchemaTO schema : schemas.values()) {
+            AttrTO attrTO = new AttrTO();
+            attrTO.setSchema(schema.getKey());
+            if (attrMap.containsKey(schema.getKey())) {
+                attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
+            } else {
+                attrTO.getValues().add(StringUtils.EMPTY);
+            }
+
+            attrs.add(attrTO);
+        }
 
-        attributesContainer.add(attributes);
+        anyTO.getVirAttrs().clear();
+        anyTO.getVirAttrs().addAll(attrs);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/layout/ConsoleLayoutInfoModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/layout/ConsoleLayoutInfoModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/layout/ConsoleLayoutInfoModal.html
new file mode 100644
index 0000000..c969faa
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/layout/ConsoleLayoutInfoModal.html
@@ -0,0 +1,54 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/javascript/javascript.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/matchbrackets.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/closebrackets.js"></script>
+    <script type="text/javascript">
+      function updateTextArea(editor) {
+        document.getElementById("consoleLayoutInfoDefForm").children["consoleLayoutInfo"].value = editor.getValue();
+      }
+    </script>
+    <style>
+      .w_content_3 {
+        padding: 0;
+        color: #333333;
+        font-family: Verdana,Tahoma,sans-serif;
+        font-size: 100%;
+        border: 1px solid #BBBBBB;
+        padding: 1%;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div style="padding: 1%;">
+      <div class="w_content_3" id="consoleLayoutInfoDefForm">
+        <textarea wicket:id="consoleLayoutInfo" id="consoleLayoutInfo" name="consoleLayoutInfo" style="width: 100%; height: 350px;">
+        </textarea>
+      </div>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
index 8217a07..2f30d1f 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/MailTemplateContentModal.html
@@ -17,39 +17,37 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:head>
-      <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="webjars/codemirror/${codemirror.version}/lib/codemirror.css"/>
 
-      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
-      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/xml/xml.js"></script>
-      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
-      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
-      <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
-      <script type="text/javascript">
-        function updateTextArea(editor) {
-          document.getElementById("templateDefForm").children["template"].value = editor.getValue();
-        }
-      </script>
-      <style>
-        .w_content_3 {
-          padding: 0;
-          color: #333333;
-          font-family: Verdana,Tahoma,sans-serif;
-          font-size: 100%;
-          border: 1px solid #BBBBBB;
-          padding: 1%;
-        }
-      </style>
-    </wicket:head>
-    <wicket:panel>
-      <div style="padding: 1%;">
-        <div class="w_content_3" id="templateDefForm">
-          <textarea wicket:id="template" id="template" name="template" style="width: 100%; height: 350px;">
-          </textarea>
-        </div>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/lib/codemirror.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/mode/xml/xml.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/closetag.js"></script>
+    <script type="text/javascript">
+      function updateTextArea(editor) {
+        document.getElementById("templateDefForm").children["template"].value = editor.getValue();
+      }
+    </script>
+    <style>
+      .w_content_3 {
+        padding: 0;
+        color: #333333;
+        font-family: Verdana,Tahoma,sans-serif;
+        font-size: 100%;
+        border: 1px solid #BBBBBB;
+        padding: 1%;
+      }
+    </style>
+  </wicket:head>
+  <wicket:panel>
+    <div style="padding: 1%;">
+      <div class="w_content_3" id="templateDefForm">
+        <textarea wicket:id="template" id="template" name="template" style="width: 100%; height: 350px;">
+        </textarea>
       </div>
-    </wicket:panel>
-  </body>
+    </div>
+  </wicket:panel>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html
index 39ab5ca..6c705a9 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/BasePage.html
@@ -107,7 +107,6 @@ under the License.
                 <li wicket:id="securityquestionsLI"><a href="#" wicket:id="securityquestions"><i class="fa fa-question"></i><wicket:message key="securityQuestions"/></a></li>
                 <li wicket:id="workflowLI"><a href="#" wicket:id="workflow"><i class="fa fa-share-alt"></i><wicket:message key="workflow"/></a></li>
                 <li wicket:id="logsLI"><a href="#" wicket:id="logs"><i class="fa fa-file-text-o"></i><wicket:message key="logs"/></a></li>
-                <li wicket:id="layoutsLI"><a href="#" wicket:id="layouts"><i class="fa fa-object-ungroup"></i><wicket:message key="layouts"/></a></li>
                 <li wicket:id="typesLI"><a href="#" wicket:id="types"><i class="fa fa-wrench"></i><wicket:message key="types"/></a></li>
                 <li wicket:id="notificationsLI"><a href="#" wicket:id="notifications"><i class="fa fa-envelope-o"></i><wicket:message key="notifications"/></a></li>
                 <li wicket:id="parametersLI"><a href="#" wicket:id="parameters"><i class="fa fa-cog"></i><wicket:message key="parameters"/></a></li>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/pages/Layouts.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Layouts.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Layouts.html
deleted file mode 100644
index a5e0c54..0000000
--- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Layouts.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <wicket:extend>
-
-    <section class="content-header">
-      <h1>
-        &nbsp;
-        <small>Work in progress</small>
-      </h1>
-      <ol class="breadcrumb">
-        <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"/></a></li>
-        <li class="active"><wicket:message key="configuration"/></li>
-        <li class="active"><wicket:message key="layouts"/></li>
-      </ol>
-    </section>
-
-    <section class="content">
-      <div class="progress progress active" style="margin:100px">
-        <div style="width: 70%" aria-valuemax="100" aria-valuemin="0" aria-valuenow="20" role="progressbar" class="progress-bar progress-bar-success progress-bar-striped">
-          <span class="sr-only">20% Complete</span>
-        </div>
-      </div>
-    </section>
-
-  </wicket:extend>
-</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/panels/XMLWorkflowEditorModalPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/XMLWorkflowEditorModalPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/XMLWorkflowEditorModalPanel.html
index 6e979ea..0b51d5e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/XMLWorkflowEditorModalPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/XMLWorkflowEditorModalPanel.html
@@ -25,6 +25,7 @@ under the License.
     <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/display/autorefresh.js"></script>
     <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/search.js"></script>
     <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/search/searchcursor.js"></script>
+    <script type="text/javascript" src="webjars/codemirror/${codemirror.version}/addon/edit/closetag.js"></script>
     <script type="text/javascript">
       function updateTextArea(editor) {
         document.getElementById("workflowDefArea").value = editor.getValue();

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
index 28ce9e7..a90a790 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
@@ -45,6 +45,7 @@ under the License.
     <span wicket:id="panelEdit">[plus]</span>
     <span wicket:id="panelHtmlEdit">[plus]</span>
     <span wicket:id="panelTextEdit">[plus]</span>
+    <span wicket:id="panelLayoutEdit">[plus]</span>
     <span wicket:id="panelExport">[plus]</span>
     <span wicket:id="panelPropagationTasks">[plus]</span>
     <span wicket:id="panelNotificationTasks">[plus]</span>
@@ -69,7 +70,7 @@ under the License.
     </wicket:fragment>
 
     <wicket:fragment wicket:id="fragmentClaim">
-      <a href="#" wicket:id="claimLink" class="btn"><i id="actionLink" class="fa fa-thumbs-o-up" alt="claim icon" title="Claim"></i></a>
+      <a href="#" wicket:id="claimLink" class="btn"><i id="actionLink" class="fa fa-ticket" alt="claim icon" title="Claim"></i></a>
     </wicket:fragment>
 
     <wicket:fragment wicket:id="fragmentManageResources">
@@ -128,6 +129,10 @@ under the License.
       <a href="#" wicket:id="textEditLink" class="btn"><i id="actionLink" class="fa fa-file-text-o" alt="text edit icon" title="TEXT Edit"></i></a>
     </wicket:fragment>
 
+    <wicket:fragment wicket:id="fragmentLayoutEdit">
+      <a href="#" wicket:id="layoutEditLink" class="btn"><i id="actionLink" class="fa fa-object-ungroup" alt="layout edit icon" title="Layout Edit"></i></a>
+    </wicket:fragment>
+
     <wicket:fragment wicket:id="fragmentReset">
       <a href="#" wicket:id="resetLink" class="btn"><img id="actionLink" src="img/actions/reset.png" alt="reset icon" title="Reset sync token"/></a>
     </wicket:fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/DerAttrs.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/DerAttrs.html b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/DerAttrs.html
index defa627..1ec59d5 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/DerAttrs.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/DerAttrs.html
@@ -16,18 +16,15 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org" >
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <div id="emptyPlaceholder"/>
-      <div wicket:id="derAttrContainer">
-        <div class="form-group" wicket:id="attrs">
-          <span wicket:id="panel">
-            [panel for dynamic input type markup]
-          </span>
-        </div>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div id="emptyPlaceholder"/>
+    <div wicket:id="schemas">
+      <div class="form-group">
+        <span wicket:id="panel">
+          [panel for dynamic input type markup]
+        </span>
       </div>
-    </wicket:panel>
-  </body>
-</html>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/VirAttrs.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/VirAttrs.html b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/VirAttrs.html
index a0d9eb3..1ec59d5 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/VirAttrs.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/VirAttrs.html
@@ -16,18 +16,15 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->
-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org" >
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <div id="emptyPlaceholder"/>
-      <div wicket:id="virAttrContainer">
-        <div class="form-group" wicket:id="attrs">
-          <span wicket:id="panel">
-            [panel for dynamic input type markup]
-          </span>
-        </div>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <div id="emptyPlaceholder"/>
+    <div wicket:id="schemas">
+      <div class="form-group">
+        <span wicket:id="panel">
+          [panel for dynamic input type markup]
+        </span>
       </div>
-    </wicket:panel>
-  </body>
-</html>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java
index 97be831..e6027f1 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowFormTO.java
@@ -38,14 +38,12 @@ public class WorkflowFormTO extends AbstractBaseBean {
 
     private static final long serialVersionUID = -7044543391316529128L;
 
-    private String userKey;
+    private String username;
 
     private String taskId;
 
     private String key;
 
-    private String description;
-
     private Date createTime;
 
     private Date dueDate;
@@ -54,12 +52,12 @@ public class WorkflowFormTO extends AbstractBaseBean {
 
     private final List<WorkflowFormPropertyTO> properties = new ArrayList<>();
 
-    public String getUserKey() {
-        return userKey;
+    public String getUsername() {
+        return username;
     }
 
-    public void setUserKey(final String userKey) {
-        this.userKey = userKey;
+    public void setUsername(final String username) {
+        this.username = username;
     }
 
     public String getTaskId() {
@@ -86,14 +84,6 @@ public class WorkflowFormTO extends AbstractBaseBean {
         this.createTime = createTime;
     }
 
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(final String description) {
-        this.description = description;
-    }
-
     public Date getDueDate() {
         return dueDate;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
index 132e27a..782a3d2 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/RoleService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.common.rest.api.service;
 
+import java.io.InputStream;
 import java.util.List;
 import javax.validation.constraints.NotNull;
 import javax.ws.rs.Consumes;
@@ -86,4 +87,35 @@ public interface RoleService extends JAXRSService {
     @DELETE
     @Path("{key}")
     void delete(@NotNull @PathParam("key") String key);
+
+    /**
+     * Gets the console layout information as JSON string for the role with the given key, if available.
+     *
+     * @param key role key
+     * @return console layout information as JSON string for the role with the given key, if available
+     */
+    @GET
+    @Path("{key}/consoleLayout")
+    @Produces({ MediaType.APPLICATION_JSON })
+    Response getConsoleLayoutInfo(@NotNull @PathParam("key") String key);
+
+    /**
+     * Sets the console layout information as JSON string for the role with the given key, if available.
+     *
+     * @param key role key
+     * @param consoleLayoutInfoIn console layout information to be set
+     */
+    @PUT
+    @Path("{key}/consoleLayout")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    void setConsoleLayoutInfo(@NotNull @PathParam("key") String key, InputStream consoleLayoutInfoIn);
+
+    /**
+     * Removes the console layout information for the role with the given key, if available.
+     *
+     * @param key role key
+     */
+    @DELETE
+    @Path("{key}/consoleLayout")
+    void removeConsoleLayoutInfo(@NotNull @PathParam("key") String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
index 92cb880..ced5dc7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
@@ -24,6 +24,7 @@ import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.to.RoleTO;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -99,6 +100,38 @@ public class RoleLogic extends AbstractTransactionalLogic<RoleTO> {
         return deleted;
     }
 
+    @PreAuthorize("isAuthenticated()")
+    public String getConsoleLayoutInfo(final String key) {
+        Role role = roleDAO.find(key);
+        if (role == null) {
+            LOG.error("Could not find role '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        String consoleLayout = role.getConsoleLayoutInfo();
+        if (StringUtils.isBlank(consoleLayout)) {
+            LOG.error("Could not find console layout for Role '" + key + "'");
+
+            throw new NotFoundException("Console layout for role " + key);
+        }
+
+        return consoleLayout;
+    }
+
+    @PreAuthorize("hasRole('" + StandardEntitlement.ROLE_UPDATE + "')")
+    public void setConsoleLayoutInfo(final String key, final String consoleLayout) {
+        Role role = roleDAO.find(key);
+        if (role == null) {
+            LOG.error("Could not find role '" + key + "'");
+
+            throw new NotFoundException(key);
+        }
+
+        role.setConsoleLayoutInfo(consoleLayout);
+        roleDAO.save(role);
+    }
+
     @Override
     protected RoleTO resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {
@@ -115,7 +148,7 @@ public class RoleLogic extends AbstractTransactionalLogic<RoleTO> {
             }
         }
 
-        if ((key != null) && !key.equals(0L)) {
+        if (key != null) {
             try {
                 return binder.getRoleTO(roleDAO.find(key));
             } catch (Throwable ignore) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/5b47ab23/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
index 60d62f1..441f87a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Role.java
@@ -33,4 +33,8 @@ public interface Role extends ProvidedKeyEntity {
     DynRoleMembership getDynMembership();
 
     void setDynMembership(DynRoleMembership dynMembership);
+
+    String getConsoleLayoutInfo();
+
+    void setConsoleLayoutInfo(String consoleLayoutInfo);
 }