syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From andreapatrice...@apache.org
Subject [syncope] branch master updated: [SYNCOPE-1421] provided customizability feature through JSON file
Date Mon, 03 Jun 2019 12:09:38 GMT
This is an automated email from the ASF dual-hosted git repository.

andreapatricelli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new e08e878  [SYNCOPE-1421] provided customizability feature through JSON file
e08e878 is described below

commit e08e87814fed00b00a897ba1d7be53cdb68e3f11
Author: Andrea Patricelli <andreapatricelli@apache.org>
AuthorDate: Mon Jun 3 14:09:26 2019 +0200

    [SYNCOPE-1421] provided customizability feature through JSON file
---
 .../syncope/client/ui/commons/BaseSession.java     | 10 +++
 .../syncope/client/ui/commons/Constants.java       |  7 ++
 ...mLayout.java => AbstractAnyFormBaseLayout.java} | 22 +-----
 .../ui/commons/layout/AbstractAnyFormLayout.java   | 76 +-------------------
 .../client/console/SyncopeConsoleSession.java      |  4 ++
 .../client/enduser/layout/CustomizationOption.java | 62 +++++++++++++++++
 .../client/enduser/layout/FormLayoutInfoUtils.java | 27 +-------
 .../client/enduser/layout/UserFormLayoutInfo.java  | 24 ++++++-
 .../apache/syncope/client/enduser/pages/Self.java  | 16 ++++-
 .../client/enduser/rest/SyncopeRestClient.java     | 12 ++++
 .../client/enduser/wizards/any/AbstractAttrs.java  | 64 +++++++++++------
 .../enduser/wizards/any/AnyWizardBuilder.java      |  4 +-
 .../client/enduser/wizards/any/DerAttrs.java       | 15 ++--
 .../client/enduser/wizards/any/PlainAttrs.java     | 80 +++++++++-------------
 .../client/enduser/wizards/any/VirAttrs.java       | 57 ++++++++-------
 15 files changed, 254 insertions(+), 226 deletions(-)

diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseSession.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseSession.java
index b7ffe82..890faaf 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseSession.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseSession.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.client.ui.commons;
 
+import javax.ws.rs.core.MediaType;
+
 public interface BaseSession {
 
     void setDomain(String domain);
@@ -26,4 +28,12 @@ public interface BaseSession {
 
     <T> T getAnonymousService(Class<T> serviceClass);
 
+    <T> T getService(Class<T> serviceClass);
+
+    <T> T getService(String etag, Class<T> serviceClass);
+
+    <T> T getService(MediaType mediaType, Class<T> serviceClass);
+
+    <T> void resetClient(Class<T> service);
+
 }
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
index 579db02..1254ed0 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
@@ -103,6 +103,13 @@ public final class Constants {
 
     public static final String NOTIFICATION_LEVEL_PARAM = "notificationLevel";
 
+    public static final String DEFAULT_USER_FORM_LAYOUT_INFO = "{\n"
+            + "    \"formClass\" : \"org.apache.syncope.client.enduser.wizards.any.UserWizardBuilder\",\n"
+            + "    \"auxClasses\" : true,\n" + "    \"groups\" : true,\n" + "    \"plainAttrs\" : true,\n"
+            + "    \"derAttrs\" : true,\n" + "    \"virAttrs\" : true,\n" + "    \"resources\" : true,\n"
+            + "    \"whichPlainAttrs\" : {},\n" + "    \"whichDerAttrs\" : {},\n" + "    \"whichVirAttrs\" : {},\n"
+            + "    \"passwordManagement\" : true\n" + "  }";
+
     public static Component getJEXLPopover(final Component caller, final TooltipConfig.Placement placement) {
         return getJEXLPopover(caller, placement, caller.getString("jexl_ex1"), caller.getString("jexl_ex2"));
     }
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormBaseLayout.java
similarity index 81%
copy from client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java
copy to client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormBaseLayout.java
index 50313ff..83b6421 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormBaseLayout.java
@@ -20,11 +20,9 @@ package org.apache.syncope.client.ui.commons.layout;
 
 import org.apache.syncope.client.ui.commons.wizards.any.AnyForm;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
 import org.apache.syncope.common.lib.to.AnyTO;
 
-public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A>> implements Serializable {
+public abstract class AbstractAnyFormBaseLayout<A extends AnyTO, F extends AnyForm<A>> implements Serializable {
 
     private static final long serialVersionUID = -6061683026789976508L;
 
@@ -36,16 +34,10 @@ public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A
 
     private boolean plainAttrs = true;
 
-    private final List<String> whichPlainAttrs = new ArrayList<>();
-
     private boolean derAttrs = true;
 
-    private final List<String> whichDerAttrs = new ArrayList<>();
-
     private boolean virAttrs = true;
 
-    private final List<String> whichVirAttrs = new ArrayList<>();
-
     private boolean resources = true;
 
     protected abstract Class<? extends F> getDefaultFormClass();
@@ -82,10 +74,6 @@ public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A
         this.plainAttrs = plainAttrs;
     }
 
-    public List<String> getWhichPlainAttrs() {
-        return whichPlainAttrs;
-    }
-
     public boolean isDerAttrs() {
         return derAttrs;
     }
@@ -94,10 +82,6 @@ public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A
         this.derAttrs = derAttrs;
     }
 
-    public List<String> getWhichDerAttrs() {
-        return whichDerAttrs;
-    }
-
     public boolean isVirAttrs() {
         return virAttrs;
     }
@@ -106,10 +90,6 @@ public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A
         this.virAttrs = virAttrs;
     }
 
-    public List<String> getWhichVirAttrs() {
-        return whichVirAttrs;
-    }
-
     public boolean isResources() {
         return resources;
     }
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java
index 50313ff..871bab6 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/layout/AbstractAnyFormLayout.java
@@ -19,103 +19,31 @@
 package org.apache.syncope.client.ui.commons.layout;
 
 import org.apache.syncope.client.ui.commons.wizards.any.AnyForm;
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.syncope.common.lib.to.AnyTO;
 
-public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A>> implements Serializable {
+public abstract class AbstractAnyFormLayout<A extends AnyTO, F extends AnyForm<A>>
+        extends AbstractAnyFormBaseLayout<A, F> {
 
     private static final long serialVersionUID = -6061683026789976508L;
 
-    private Class<F> formClass;
-
-    private boolean auxClasses = true;
-
-    private boolean groups = true;
-
-    private boolean plainAttrs = true;
-
     private final List<String> whichPlainAttrs = new ArrayList<>();
 
-    private boolean derAttrs = true;
-
     private final List<String> whichDerAttrs = new ArrayList<>();
 
-    private boolean virAttrs = true;
-
     private final List<String> whichVirAttrs = new ArrayList<>();
 
-    private boolean resources = true;
-
-    protected abstract Class<? extends F> getDefaultFormClass();
-
-    public Class<? extends F> getFormClass() {
-        return formClass == null ? getDefaultFormClass() : formClass;
-    }
-
-    public void setFormClass(final Class<F> formClass) {
-        this.formClass = formClass;
-    }
-
-    public boolean isAuxClasses() {
-        return auxClasses;
-    }
-
-    public void setAuxClasses(final boolean auxClasses) {
-        this.auxClasses = auxClasses;
-    }
-
-    public boolean isGroups() {
-        return groups;
-    }
-
-    public void setGroups(final boolean groups) {
-        this.groups = groups;
-    }
-
-    public boolean isPlainAttrs() {
-        return plainAttrs;
-    }
-
-    public void setPlainAttrs(final boolean plainAttrs) {
-        this.plainAttrs = plainAttrs;
-    }
-
     public List<String> getWhichPlainAttrs() {
         return whichPlainAttrs;
     }
 
-    public boolean isDerAttrs() {
-        return derAttrs;
-    }
-
-    public void setDerAttrs(final boolean derAttrs) {
-        this.derAttrs = derAttrs;
-    }
-
     public List<String> getWhichDerAttrs() {
         return whichDerAttrs;
     }
 
-    public boolean isVirAttrs() {
-        return virAttrs;
-    }
-
-    public void setVirAttrs(final boolean virAttrs) {
-        this.virAttrs = virAttrs;
-    }
-
     public List<String> getWhichVirAttrs() {
         return whichVirAttrs;
     }
 
-    public boolean isResources() {
-        return resources;
-    }
-
-    public void setResources(final boolean resources) {
-        this.resources = resources;
-    }
-
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
index 5383bc6..84a8adb 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
@@ -308,10 +308,12 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
         return getAnonymousClient().getService(serviceClass);
     }
 
+    @Override
     public <T> T getService(final Class<T> serviceClass) {
         return getCachedService(serviceClass);
     }
 
+    @Override
     public <T> T getService(final String etag, final Class<T> serviceClass) {
         T serviceInstance = getCachedService(serviceClass);
         WebClient.client(serviceInstance).match(new EntityTag(etag), false);
@@ -319,6 +321,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
         return serviceInstance;
     }
 
+    @Override
     public <T> T getService(final MediaType mediaType, final Class<T> serviceClass) {
         T service;
 
@@ -338,6 +341,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
         return client.batch();
     }
 
+    @Override
     public <T> void resetClient(final Class<T> service) {
         T serviceInstance = getCachedService(service);
         WebClient.client(serviceInstance).reset();
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java
new file mode 100644
index 0000000..c716f07
--- /dev/null
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/CustomizationOption.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+package org.apache.syncope.client.enduser.layout;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomizationOption implements Serializable {
+
+    private static final long serialVersionUID = 4910266842123376686L;
+
+    private boolean readonly;
+
+    private List<String> defaultValues = new ArrayList<>();
+
+    public CustomizationOption() {
+    }
+
+    public boolean isReadonly() {
+        return readonly;
+    }
+
+    public void setReadonly(final boolean readonly) {
+        this.readonly = readonly;
+    }
+
+    public List<String> getDefaultValues() {
+        return defaultValues;
+    }
+
+    public void setDefaultValues(final List<String> defaultValues) {
+        this.defaultValues = defaultValues;
+    }
+
+    public CustomizationOption readonly(final Boolean value) {
+        this.readonly = value;
+        return this;
+    }
+
+    public CustomizationOption defaultValues(final List<String> value) {
+        this.defaultValues = value;
+        return this;
+    }
+
+}
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/FormLayoutInfoUtils.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/FormLayoutInfoUtils.java
index c921e9e..f5517bb 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/FormLayoutInfoUtils.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/FormLayoutInfoUtils.java
@@ -18,16 +18,13 @@
  */
 package org.apache.syncope.client.enduser.layout;
 
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
-import java.util.Collection;
 import java.util.List;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.rest.RoleRestClient;
 import org.apache.syncope.client.ui.commons.wizards.ModalPanelBuilder;
 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
 import org.apache.syncope.common.lib.to.UserTO;
@@ -39,31 +36,11 @@ import org.apache.wicket.PageReference;
  */
 public final class FormLayoutInfoUtils {
 
-    private static final RoleRestClient ROLE_REST_CLIENT = new RoleRestClient();
-
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
-    public static UserFormLayoutInfo fetch(
-            final Collection<String> anyTypes) {
-
-        List<String> ownedRoles = SyncopeEnduserSession.get().getSelfTO().getRoles();
+    public static UserFormLayoutInfo fromJsonString(final String content) {
         try {
-            JsonNode tree = null;
-            for (int i = 0; i < ownedRoles.size() && tree == null; i++) {
-                String consoleLayoutInfo = ROLE_REST_CLIENT.readConsoleLayoutInfo(ownedRoles.get(i));
-                if (StringUtils.isNotBlank(consoleLayoutInfo)) {
-                    tree = MAPPER.readTree(consoleLayoutInfo);
-                }
-            }
-            if (tree == null) {
-                tree = MAPPER.createObjectNode();
-            }
-
-            UserFormLayoutInfo userFormLayoutInfo = tree.has(AnyTypeKind.USER.name())
-                    ? MAPPER.treeToValue(tree.get(AnyTypeKind.USER.name()), UserFormLayoutInfo.class)
-                    : new UserFormLayoutInfo();
-
-            return userFormLayoutInfo;
+            return MAPPER.readValue(content, UserFormLayoutInfo.class);
         } catch (IOException e) {
             throw new IllegalArgumentException("While parsing console layout info for "
                     + SyncopeEnduserSession.get().getSelfTO().getUsername(), e);
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
index 865b931..477c1d1 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/layout/UserFormLayoutInfo.java
@@ -18,17 +18,37 @@
  */
 package org.apache.syncope.client.enduser.layout;
 
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.syncope.client.enduser.wizards.any.UserWizardBuilder;
-import org.apache.syncope.client.ui.commons.layout.AbstractAnyFormLayout;
+import org.apache.syncope.client.ui.commons.layout.AbstractAnyFormBaseLayout;
 import org.apache.syncope.client.ui.commons.layout.UserForm;
 import org.apache.syncope.common.lib.to.UserTO;
 
-public class UserFormLayoutInfo extends AbstractAnyFormLayout<UserTO, UserForm> {
+public class UserFormLayoutInfo extends AbstractAnyFormBaseLayout<UserTO, UserForm> {
 
     private static final long serialVersionUID = -5573691733739618500L;
 
+    private final Map<String, CustomizationOption> whichPlainAttrs = new HashMap<>();
+
+    private final Map<String, CustomizationOption> whichDerAttrs = new HashMap<>();
+
+    private final Map<String, CustomizationOption> whichVirAttrs = new HashMap<>();
+
     private boolean passwordManagement = true;
 
+    public Map<String, CustomizationOption> getWhichPlainAttrs() {
+        return whichPlainAttrs;
+    }
+
+    public Map<String, CustomizationOption> getWhichDerAttrs() {
+        return whichDerAttrs;
+    }
+
+    public Map<String, CustomizationOption> getWhichVirAttrs() {
+        return whichVirAttrs;
+    }
+
     @Override
     protected Class<? extends UserForm> getDefaultFormClass() {
         return UserWizardBuilder.class;
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java
index bc3a1ed..ca78a4d 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/pages/Self.java
@@ -19,14 +19,17 @@
 package org.apache.syncope.client.enduser.pages;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.SyncopeWebApplication;
+import org.apache.syncope.client.enduser.layout.FormLayoutInfoUtils;
 import org.apache.syncope.client.enduser.layout.UserFormLayoutInfo;
 import org.apache.syncope.client.enduser.wizards.any.UserWizardBuilder;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
+import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
@@ -34,11 +37,15 @@ import org.apache.wicket.event.IEvent;
 import org.apache.wicket.event.IEventSource;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
+import org.apache.wicket.spring.injection.annot.SpringBean;
 
 public class Self extends BaseEnduserWebPage implements IEventSource {
 
     private static final long serialVersionUID = 164651008547631054L;
 
+    @SpringBean
+    private ConfParamOps confParamOps;
+
     private UserWizardBuilder userWizardBuilder;
 
     protected static final ObjectMapper MAPPER = new ObjectMapper();
@@ -88,11 +95,18 @@ public class Self extends BaseEnduserWebPage implements IEventSource {
     }
 
     protected final AjaxWizard<AnyWrapper<UserTO>> buildWizard(final UserTO userTO, final AjaxWizard.Mode mode) {
+        final String formLayoutConfParam = confParamOps.get(
+                SyncopeEnduserSession.get().getDomain(),
+                "enduser.form.layout.info",
+                Constants.DEFAULT_USER_FORM_LAYOUT_INFO,
+                String.class);
         userWizardBuilder = new UserWizardBuilder(
                 null,
                 userTO,
                 SyncopeEnduserSession.get().getService(SyncopeService.class).platform().getUserClasses(),
-                new UserFormLayoutInfo(),
+                StringUtils.isBlank(formLayoutConfParam)
+                ? new UserFormLayoutInfo()
+                : FormLayoutInfoUtils.fromJsonString(formLayoutConfParam),
                 this.getPageReference());
         userWizardBuilder.setItem(new UserWrapper(userTO));
         return userWizardBuilder.build(WIZARD_ID, mode);
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
index 1f88621..3aa77a8 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/rest/SyncopeRestClient.java
@@ -21,6 +21,7 @@ package org.apache.syncope.client.enduser.rest;
 import java.util.Collections;
 import java.util.List;
 import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.TypeExtensionTO;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
 
 public class SyncopeRestClient extends BaseRestClient {
@@ -37,4 +38,15 @@ public class SyncopeRestClient extends BaseRestClient {
         }
         return types;
     }
+
+    public List<String> searchUserTypeExtensions(final String groupName) {
+        List<String> types = Collections.emptyList();
+        try {
+            TypeExtensionTO typeExtensionTO = getService(SyncopeService.class).readUserTypeExtension(groupName);
+            types = typeExtensionTO == null ? types : typeExtensionTO.getAuxClasses();
+        } catch (SyncopeClientException e) {
+            LOG.error("While reading all any type classes for group [{}]", groupName, e);
+        }
+        return types;
+    }
 }
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java
index aa6c780..7e64f40 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AbstractAttrs.java
@@ -26,14 +26,15 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.common.util.StringUtils;
-import org.apache.syncope.client.enduser.rest.GroupRestClient;
+import org.apache.syncope.client.enduser.layout.CustomizationOption;
 import org.apache.syncope.client.enduser.rest.SchemaRestClient;
+import org.apache.syncope.client.enduser.rest.SyncopeRestClient;
 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.Attr;
-import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
 import org.apache.syncope.common.lib.types.SchemaType;
 import org.apache.wicket.PageReference;
@@ -55,11 +56,11 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
 
     private final SchemaRestClient schemaRestClient = new SchemaRestClient();
 
-    private final GroupRestClient groupRestClient = new GroupRestClient();
+    private final SyncopeRestClient syncopeRestClient = new SyncopeRestClient();
 
     protected final AnyTO anyTO;
 
-    private final List<String> whichAttrs;
+    private final Map<String, CustomizationOption> whichAttrs;
 
     protected final Map<String, S> schemas = new LinkedHashMap<>();
 
@@ -74,7 +75,7 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
     public AbstractAttrs(
             final AnyWrapper<?> modelObject,
             final List<String> anyTypeClasses,
-            final List<String> whichAttrs) {
+            final Map<String, CustomizationOption> whichAttrs) {
         super();
         this.anyTypeClasses = anyTypeClasses;
         this.attrs = new ListModel<>(Collections.<Attr>emptyList());
@@ -103,7 +104,8 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
 
             for (MembershipTO membership : (List<MembershipTO>) PropertyResolver.getPropertyField(
                     "memberships", anyTO).get(anyTO)) {
-                setSchemas(membership.getGroupKey(), getMembershipAuxClasses(membership, anyTO.getType()));
+                setSchemas(Pair.of(membership.getGroupKey(), membership.getGroupName()), getMembershipAuxClasses(
+                        membership, anyTO.getType()));
                 setAttrs(membership);
 
                 if (AbstractAttrs.this instanceof PlainAttrs && !membership.getPlainAttrs().isEmpty()) {
@@ -121,29 +123,48 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
         return memberships;
     }
 
-    protected boolean reoderSchemas() {
+    protected boolean filterSchemas() {
         return !whichAttrs.isEmpty();
     }
 
+    protected boolean renderAsReadonly(final String schema, final String groupName) {
+        // whether to render the attribute as readonly or not, without considering schema readonly property
+        String schemaName = (org.apache.commons.lang3.StringUtils.isBlank(groupName)
+                ? org.apache.commons.lang3.StringUtils.EMPTY
+                : groupName + "#")
+                + schema;
+        return whichAttrs.get(schemaName) == null ? false : whichAttrs.get(schemaName).isReadonly();
+    }
+
+    protected List<String> getDefaultValues(final String schema, final String groupName) {
+        String schemaName = (org.apache.commons.lang3.StringUtils.isBlank(groupName)
+                ? org.apache.commons.lang3.StringUtils.EMPTY
+                : groupName + "#")
+                + schema;
+        return whichAttrs.get(schemaName) == null
+                ? Collections.<String>emptyList()
+                : whichAttrs.get(schemaName).getDefaultValues();
+    }
+
     protected abstract SchemaType getSchemaType();
 
-    private void setSchemas(final String membership, final List<String> anyTypeClasses) {
+    private void setSchemas(final Pair<String, String> membership, final List<String> anyTypeClasses) {
         final Map<String, S> mscs;
 
-        if (membershipSchemas.containsKey(membership)) {
-            mscs = membershipSchemas.get(membership);
+        if (membershipSchemas.containsKey(membership.getKey())) {
+            mscs = membershipSchemas.get(membership.getKey());
         } else {
             mscs = new LinkedHashMap<>();
-            membershipSchemas.put(membership, mscs);
+            membershipSchemas.put(membership.getKey(), mscs);
         }
-        setSchemas(anyTypeClasses, mscs);
+        setSchemas(anyTypeClasses, membership.getValue(), mscs);
     }
 
     private void setSchemas(final List<String> anyTypeClasses) {
-        setSchemas(anyTypeClasses, schemas);
+        setSchemas(anyTypeClasses, null, schemas);
     }
 
-    private void setSchemas(final List<String> anyTypeClasses, final Map<String, S> scs) {
+    private void setSchemas(final List<String> anyTypeClasses, final String groupName, final Map<String, S> scs) {
         final List<S> allSchemas;
         if (anyTypeClasses.isEmpty()) {
             allSchemas = Collections.emptyList();
@@ -153,10 +174,12 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
 
         scs.clear();
 
-        if (reoderSchemas()) {
+        if (filterSchemas()) {
             // 1. remove attributes not selected for display
             allSchemas.removeAll(allSchemas.stream().
-                    filter(schemaTO -> !whichAttrs.contains(schemaTO.getKey())).collect(Collectors.toSet()));
+                    filter(schemaTO -> org.apache.commons.lang3.StringUtils.isBlank(groupName)
+                    ? !whichAttrs.containsKey(schemaTO.getKey())
+                    : !whichAttrs.containsKey(groupName + "#" + schemaTO.getKey())).collect(Collectors.toSet()));
         }
 
         allSchemas.forEach(schemaTO -> {
@@ -185,8 +208,7 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
 
     protected List<String> getMembershipAuxClasses(final MembershipTO membershipTO, final String anyType) {
         try {
-            final GroupTO groupTO = groupRestClient.read(membershipTO.getGroupKey());
-            return groupTO.getTypeExtension(anyType).get().getAuxClasses();
+            return syncopeRestClient.searchUserTypeExtensions(membershipTO.getGroupName());
         } catch (Exception e) {
             return Collections.emptyList();
         }
@@ -216,9 +238,9 @@ public abstract class AbstractAttrs<S extends SchemaTO> extends WizardStep imple
             }
             if (right == null || StringUtils.isEmpty(right.getSchema())) {
                 return 1;
-            } else if (AbstractAttrs.this.reoderSchemas()) {
-                int leftIndex = AbstractAttrs.this.whichAttrs.indexOf(left.getSchema());
-                int rightIndex = AbstractAttrs.this.whichAttrs.indexOf(right.getSchema());
+            } else if (AbstractAttrs.this.filterSchemas()) {
+                int leftIndex = new ArrayList<>(AbstractAttrs.this.whichAttrs.keySet()).indexOf(left.getSchema());
+                int rightIndex = new ArrayList<>(AbstractAttrs.this.whichAttrs.keySet()).indexOf(right.getSchema());
 
                 if (leftIndex > rightIndex) {
                     return 1;
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java
index 25c1cb0..ba07c04 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/AnyWizardBuilder.java
@@ -124,18 +124,16 @@ public abstract class AnyWizardBuilder extends AbstractAnyWizardBuilder<UserTO>
 
             });
         }
-        if (formLayoutInfo.isDerAttrs() && mode != AjaxWizard.Mode.TEMPLATE) {
+        if (formLayoutInfo.isDerAttrs()) {
             wizardModel.add(new DerAttrs(modelObject, anyTypeClasses, formLayoutInfo.getWhichDerAttrs()));
         }
         if (formLayoutInfo.isVirAttrs()) {
             wizardModel.add(new VirAttrs(
                     modelObject, mode, anyTypeClasses, formLayoutInfo.getWhichVirAttrs()));
         }
-
         if (formLayoutInfo.isResources()) {
             wizardModel.add(new Resources(modelObject));
         }
-
         if (SyncopeWebApplication.get().isCaptchaEnabled()) {
             // add captcha
             captcha = new Captcha();
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java
index e4f60ed..fdd802e 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/DerAttrs.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.layout.CustomizationOption;
 import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.common.lib.EntityTOUtils;
@@ -56,7 +57,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
     public <T extends AnyTO> DerAttrs(
             final AnyWrapper<T> modelObject,
             final List<String> anyTypeClasses,
-            final List<String> whichDerAttrs) {
+            final Map<String, CustomizationOption> whichDerAttrs) {
 
         super(modelObject, anyTypeClasses, whichDerAttrs);
         setTitleModel(new ResourceModel("attributes.derived"));
@@ -116,7 +117,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
 
     @Override
     protected void setAttrs() {
-        List<Attr> attrs = new ArrayList<>();
+        List<Attr> derAttrs = new ArrayList<>();
 
         Map<String, Attr> attrMap = EntityTOUtils.buildAttrMap(anyTO.getDerAttrs());
 
@@ -127,16 +128,16 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
                 attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
             }
 
-            attrs.add(attrTO);
+            derAttrs.add(attrTO);
         });
 
         anyTO.getDerAttrs().clear();
-        anyTO.getDerAttrs().addAll(attrs);
+        anyTO.getDerAttrs().addAll(derAttrs);
     }
 
     @Override
     protected void setAttrs(final MembershipTO membershipTO) {
-        List<Attr> attrs = new ArrayList<>();
+        List<Attr> derAttrs = new ArrayList<>();
 
         final Map<String, Attr> attrMap;
         if (GroupableRelatableTO.class.cast(anyTO).getMembership(membershipTO.getGroupKey()).isPresent()) {
@@ -153,11 +154,11 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
                 attrTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
             }
 
-            attrs.add(attrTO);
+            derAttrs.add(attrTO);
         });
 
         membershipTO.getDerAttrs().clear();
-        membershipTO.getDerAttrs().addAll(attrs);
+        membershipTO.getDerAttrs().addAll(derAttrs);
     }
 
     public class DerSchemas extends Schemas {
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java
index 5371399..d749fa2 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/PlainAttrs.java
@@ -25,17 +25,15 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.stream.Collectors;
-import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.time.FastDateFormat;
+import org.apache.syncope.client.enduser.layout.CustomizationOption;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateFieldPanel;
 import org.apache.syncope.client.enduser.markup.html.form.BinaryFieldPanel;
 import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
 import org.apache.syncope.client.ui.commons.SchemaUtils;
-import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
@@ -87,7 +85,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
             final Form<?> form,
             final AjaxWizard.Mode mode,
             final List<String> anyTypeClasses,
-            final List<String> whichPlainAttrs) throws IllegalArgumentException {
+            final Map<String, CustomizationOption> whichPlainAttrs) throws IllegalArgumentException {
 
         super(modelObject, anyTypeClasses, whichPlainAttrs);
         this.mode = mode;
@@ -138,6 +136,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
                     public WebMarkupContainer getPanel(final String panelId) {
                         return new PlainSchemas(
                                 panelId,
+                                membershipTO.getGroupName(),
                                 membershipSchemas.get(membershipTO.getGroupKey()),
                                 new ListModel<>(getAttrsFromTO(membershipTO)));
                     }
@@ -152,8 +151,8 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
     }
 
     @Override
-    protected boolean reoderSchemas() {
-        return super.reoderSchemas() && mode != AjaxWizard.Mode.TEMPLATE;
+    protected boolean filterSchemas() {
+        return super.filterSchemas() && mode != AjaxWizard.Mode.TEMPLATE;
     }
 
     @Override
@@ -189,7 +188,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
 
     @Override
     protected void setAttrs(final MembershipTO membershipTO) {
-        List<Attr> attrs = new ArrayList<>();
+        List<Attr> plainAttrs = new ArrayList<>();
 
         final Map<String, Attr> attrMap;
         if (GroupableRelatableTO.class.cast(anyTO).getMembership(membershipTO.getGroupKey()).isPresent()) {
@@ -199,7 +198,7 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
             attrMap = new HashMap<>();
         }
 
-        attrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().
+        plainAttrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().
                 map(schema -> {
                     Attr attrTO = new Attr();
                     attrTO.setSchema(schema.getKey());
@@ -212,28 +211,20 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
                 }).collect(Collectors.toList()));
 
         membershipTO.getPlainAttrs().clear();
-        membershipTO.getPlainAttrs().addAll(attrs);
+        membershipTO.getPlainAttrs().addAll(plainAttrs);
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
     protected FieldPanel getFieldPanel(final PlainSchemaTO schemaTO) {
-        final boolean required;
-        final boolean readOnly;
-        final AttrSchemaType type;
-        final boolean jexlHelp;
-
-        if (mode == AjaxWizard.Mode.TEMPLATE) {
-            required = false;
-            readOnly = false;
-            type = AttrSchemaType.String;
-            jexlHelp = true;
-        } else {
-            required = schemaTO.getMandatoryCondition().equalsIgnoreCase("true");
-            readOnly = schemaTO.isReadonly();
-            type = schemaTO.getType();
-            jexlHelp = false;
+        return getFieldPanel(schemaTO, null);
+    }
 
-        }
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    protected FieldPanel getFieldPanel(final PlainSchemaTO schemaTO, final String groupName) {
+        final boolean required = schemaTO.getMandatoryCondition().equalsIgnoreCase("true");
+        final boolean readOnly = schemaTO.isReadonly() || renderAsReadonly(schemaTO.getKey(), groupName);
+        final AttrSchemaType type = schemaTO.getType();
+        final boolean jexlHelp = false;
 
         FieldPanel panel;
         switch (type) {
@@ -387,6 +378,14 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
                 final String id,
                 final Map<String, PlainSchemaTO> schemas,
                 final IModel<List<Attr>> attrTOs) {
+            this(id, null, schemas, attrTOs);
+        }
+
+        public PlainSchemas(
+                final String id,
+                final String groupName,
+                final Map<String, PlainSchemaTO> schemas,
+                final IModel<List<Attr>> attrTOs) {
             super(id);
 
             add(new ListView<Attr>("schemas", attrTOs) {
@@ -398,11 +397,15 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
                 protected void populateItem(final ListItem<Attr> item) {
                     Attr attrTO = item.getModelObject();
 
+                    // set default values, if any
+                    if (attrTO.getValues().stream().filter(value -> StringUtils.isNotBlank(value))
+                            .collect(Collectors.toList()).isEmpty()) {
+                        attrTO.getValues().clear();
+                        attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema(), groupName));
+                    }
+
                     AbstractFieldPanel<?> panel = getFieldPanel(schemas.get(attrTO.getSchema()));
-                    if (mode == AjaxWizard.Mode.TEMPLATE
-                            || !schemas.get(attrTO.getSchema()).isMultivalue()) {
-                        FieldPanel.class.cast(panel).setNewModel(attrTO.getValues());
-                    } else {
+                    if (schemas.get(attrTO.getSchema()).isMultivalue()) {
                         panel = new MultiFieldPanel.Builder<>(
                                 new PropertyModel<>(attrTO, "values")).build(
                                 "panel",
@@ -410,25 +413,10 @@ public class PlainAttrs extends AbstractAttrs<PlainSchemaTO> {
                                 FieldPanel.class.cast(panel));
                         // SYNCOPE-1215 the entire multifield panel must be readonly, not only its field
                         ((MultiFieldPanel) panel).setReadOnly(schemas.get(attrTO.getSchema()).isReadonly());
+                    } else {
+                        FieldPanel.class.cast(panel).setNewModel(attrTO.getValues());
                     }
                     item.add(panel);
-
-                    Optional<Attr> prevAttr = previousObject == null
-                            ? Optional.empty()
-                            : previousObject.getPlainAttr(attrTO.getSchema());
-                    if (previousObject != null
-                            && ((!prevAttr.isPresent() && attrTO.getValues().stream().anyMatch(StringUtils::isNotBlank))
-                            || (prevAttr.isPresent() && !ListUtils.isEqualList(
-                            prevAttr.get().getValues().stream().
-                                    filter(StringUtils::isNotBlank).collect(Collectors.toList()),
-                            attrTO.getValues().stream().
-                                    filter(StringUtils::isNotBlank).collect(Collectors.toList()))))) {
-
-                        List<String> oldValues = prevAttr.isPresent()
-                                ? prevAttr.get().getValues()
-                                : Collections.<String>emptyList();
-                        panel.showExternAction(new LabelInfo("externalAction", oldValues));
-                    }
                 }
             });
         }
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java
index 9dec684..4c46597 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/wizards/any/VirAttrs.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.enduser.wizards.any;
 
-import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
 import org.apache.syncope.client.ui.commons.wizards.any.AnyWrapper;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,8 +27,8 @@ import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
+import org.apache.syncope.client.enduser.layout.CustomizationOption;
 import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
-import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
@@ -65,7 +64,7 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
             final AnyWrapper<T> modelObject,
             final AjaxWizard.Mode mode,
             final List<String> anyTypeClasses,
-            final List<String> whichVirAttrs) {
+            final Map<String, CustomizationOption> whichVirAttrs) {
 
         super(modelObject, anyTypeClasses, whichVirAttrs);
         this.mode = mode;
@@ -101,6 +100,7 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
                             public WebMarkupContainer getPanel(final String panelId) {
                                 return new VirAttrs.VirSchemas(
                                         panelId,
+                                        membershipTO.getGroupName(),
                                         membershipSchemas.get(membershipTO.getGroupKey()),
                                         new ListModel<>(getAttrsFromTO(membershipTO)));
                             }
@@ -126,11 +126,11 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
 
     @Override
     protected void setAttrs() {
-        List<Attr> attrs = new ArrayList<>();
+        List<Attr> virAttrs = new ArrayList<>();
 
         Map<String, Attr> attrMap = EntityTOUtils.buildAttrMap(anyTO.getVirAttrs());
 
-        attrs.addAll(schemas.values().stream().map(schema -> {
+        virAttrs.addAll(schemas.values().stream().map(schema -> {
             Attr attrTO = new Attr();
             attrTO.setSchema(schema.getKey());
             if (attrMap.containsKey(schema.getKey())) {
@@ -142,12 +142,12 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
         }).collect(Collectors.toList()));
 
         anyTO.getVirAttrs().clear();
-        anyTO.getVirAttrs().addAll(attrs);
+        anyTO.getVirAttrs().addAll(virAttrs);
     }
 
     @Override
     protected void setAttrs(final MembershipTO membershipTO) {
-        List<Attr> attrs = new ArrayList<>();
+        List<Attr> virAttrs = new ArrayList<>();
 
         final Map<String, Attr> attrMap;
         if (GroupableRelatableTO.class.cast(anyTO).getMembership(membershipTO.getGroupKey()).isPresent()) {
@@ -157,7 +157,7 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
             attrMap = new HashMap<>();
         }
 
-        attrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().map(schema -> {
+        virAttrs.addAll(membershipSchemas.get(membershipTO.getGroupKey()).values().stream().map(schema -> {
             Attr attrTO = new Attr();
             attrTO.setSchema(schema.getKey());
             if (attrMap.containsKey(schema.getKey())) {
@@ -169,7 +169,7 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
         }).collect(Collectors.toList()));
 
         membershipTO.getVirAttrs().clear();
-        membershipTO.getVirAttrs().addAll(attrs);
+        membershipTO.getVirAttrs().addAll(virAttrs);
     }
 
     public class VirSchemas extends Schemas {
@@ -180,6 +180,14 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
                 final String id,
                 final Map<String, VirSchemaTO> schemas,
                 final IModel<List<Attr>> attrTOs) {
+            this(id, null, schemas, attrTOs);
+        }
+
+        public VirSchemas(
+                final String id,
+                final String groupName,
+                final Map<String, VirSchemaTO> schemas,
+                final IModel<List<Attr>> attrTOs) {
             super(id);
 
             add(new ListView<Attr>("schemas", attrTOs) {
@@ -190,6 +198,14 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
                 @SuppressWarnings("unchecked")
                 protected void populateItem(final ListItem<Attr> item) {
                     Attr attrTO = item.getModelObject();
+
+                    // set default values, if any
+                    if (attrTO.getValues().stream().filter(value -> StringUtils.isNotBlank(value))
+                            .collect(Collectors.toList()).isEmpty()) {
+                        attrTO.getValues().clear();
+                        attrTO.getValues().addAll(getDefaultValues(attrTO.getSchema(), groupName));
+                    }
+
                     VirSchemaTO virSchemaTO = schemas.get(attrTO.getSchema());
 
                     AbstractFieldPanel<?> panel = new AjaxTextFieldPanel(
@@ -198,25 +214,14 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
                             new Model<>(),
                             false);
 
-                    if (mode == AjaxWizard.Mode.TEMPLATE) {
-                        AjaxTextFieldPanel.class.cast(panel).enableJexlHelp().setEnabled(!virSchemaTO.isReadonly());
-                    } else {
-                        panel = new MultiFieldPanel.Builder<>(
-                                new PropertyModel<List<String>>(attrTO, "values")).build(
-                                "panel",
-                                virSchemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
-                                AjaxTextFieldPanel.class.cast(panel));
-                        panel.setEnabled(!virSchemaTO.isReadonly());
-                    }
+                    panel = new MultiFieldPanel.Builder<>(
+                            new PropertyModel<List<String>>(attrTO, "values")).build(
+                            "panel",
+                            virSchemaTO.getLabel(SyncopeEnduserSession.get().getLocale()),
+                            AjaxTextFieldPanel.class.cast(panel));
+                    panel.setEnabled(!virSchemaTO.isReadonly() && !renderAsReadonly(attrTO.getSchema(), groupName));
 
                     item.add(panel);
-
-                    if (!attrTO.getValues().isEmpty()
-                            && VirAttrs.this.modelObject instanceof UserWrapper
-                            && UserWrapper.class.cast(VirAttrs.this.modelObject).getPreviousUserTO() != null) {
-
-                        panel.showExternAction(new LabelInfo("externalAction", StringUtils.EMPTY));
-                    }
                 }
             });
         }


Mime
View raw message