Repository: syncope
Updated Branches:
  refs/heads/2_0_X a8d5d0527 -> 78b68bf4b


[SYNCOPE-1053] Handling previous object and new object during approval


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/78b68bf4
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/78b68bf4
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/78b68bf4

Branch: refs/heads/2_0_X
Commit: 78b68bf4b84a1ad79d4ba246f4030e02344cbbfb
Parents: a8d5d05
Author: fmartelli <fabio.marte...@gmail.com>
Authored: Tue Jun 13 14:04:38 2017 +0200
Committer: fmartelli <fabio.marte...@gmail.com>
Committed: Fri Jun 16 09:42:16 2017 +0200

----------------------------------------------------------------------
 .../console/approvals/ApprovalDetails.java      | 18 +++++-
 .../console/layout/FormLayoutInfoUtils.java     | 27 ++++++---
 .../wicket/ajax/markup/html/LabelInfo.java      | 62 +++++++++++++++++++
 .../markup/html/form/AbstractFieldPanel.java    |  3 +-
 .../markup/html/form/AjaxSpinnerFieldPanel.java |  6 +-
 .../console/wizards/any/AbstractAttrs.java      |  9 ++-
 .../console/wizards/any/AnyWizardBuilder.java   | 16 ++---
 .../client/console/wizards/any/AuxClasses.java  | 22 ++++++-
 .../client/console/wizards/any/DerAttrs.java    |  4 +-
 .../client/console/wizards/any/Groups.java      | 64 +++++++++++++++++---
 .../client/console/wizards/any/PlainAttrs.java  | 50 ++++++++++++---
 .../console/wizards/any/Relationships.java      | 27 ++++++++-
 .../client/console/wizards/any/Resources.java   | 18 +++++-
 .../client/console/wizards/any/Roles.java       | 21 ++++++-
 .../client/console/wizards/any/UserDetails.java |  7 +++
 .../wizards/any/UserTemplateWizardBuilder.java  |  2 +-
 .../console/wizards/any/UserWizardBuilder.java  | 27 ++++++++-
 .../client/console/wizards/any/UserWrapper.java | 10 +++
 .../client/console/wizards/any/VirAttrs.java    | 32 +++++++---
 .../client/console/wizards/any/AuxClasses.html  |  1 +
 .../client/console/wizards/any/Groups.html      |  1 +
 .../console/wizards/any/Relationships.html      |  4 ++
 .../client/console/wizards/any/Resources.html   |  1 +
 .../client/console/wizards/any/Roles.html       |  3 +-
 .../syncope/common/lib/AnyOperations.java       | 15 +----
 25 files changed, 376 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
index d2d9c41..c14dab9 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
@@ -21,9 +21,10 @@ package org.apache.syncope.client.console.approvals;
 import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
 import org.apache.syncope.client.console.panels.MultilevelPanel;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
-import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.client.console.wizards.AjaxWizard;
 import org.apache.syncope.client.console.wizards.any.UserWizardBuilder;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.WorkflowFormTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.wicket.PageReference;
@@ -35,7 +36,20 @@ public class ApprovalDetails extends 
MultilevelPanel.SecondLevel {
     public ApprovalDetails(final PageReference pageRef, final WorkflowFormTO 
formTO) {
         super(MultilevelPanel.SECOND_LEVEL_ID);
 
-        add(new UserWizardBuilder(new 
UserRestClient().read(formTO.getUsername()),
+        final UserTO newUserTO;
+        final UserTO previousUserTO;
+        if (formTO.getUserPatch() == null) {
+            newUserTO = formTO.getUserTO();
+            previousUserTO = null;
+        } else {
+            formTO.getUserTO().setKey(formTO.getUserPatch().getKey());
+            newUserTO = AnyOperations.patch(formTO.getUserTO(), 
formTO.getUserPatch());
+            previousUserTO = formTO.getUserTO();
+        }
+
+        add(new UserWizardBuilder(
+                previousUserTO,
+                newUserTO,
                 new 
AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
                 new UserFormLayoutInfo(),
                 pageRef).

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/layout/FormLayoutInfoUtils.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/layout/FormLayoutInfoUtils.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/layout/FormLayoutInfoUtils.java
index a5caccb..58afe2c 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/layout/FormLayoutInfoUtils.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/layout/FormLayoutInfoUtils.java
@@ -22,6 +22,7 @@ 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.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -31,6 +32,7 @@ import 
org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.rest.RoleRestClient;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.wicket.PageReference;
 
@@ -127,13 +129,24 @@ public final class FormLayoutInfoUtils {
             final PageReference pageRef) {
 
         try {
-            return anyFormLayout.getFormClass().getConstructor(
-                    anyTO.getClass(),
-                    List.class,
-                    anyFormLayout.getClass(),
-                    pageRef.getClass()).
-                    newInstance(anyTO, anyTypeClasses, anyFormLayout, pageRef);
-        } catch (Exception e) {
+            if (anyTO instanceof UserTO) {
+                return anyFormLayout.getFormClass().getConstructor(
+                        anyTO.getClass(), // previous
+                        anyTO.getClass(), // actual
+                        List.class,
+                        anyFormLayout.getClass(),
+                        pageRef.getClass()).
+                        newInstance(null, anyTO, anyTypeClasses, 
anyFormLayout, pageRef);
+            } else {
+                return anyFormLayout.getFormClass().getConstructor(
+                        anyTO.getClass(), // actual
+                        List.class,
+                        anyFormLayout.getClass(),
+                        pageRef.getClass()).
+                        newInstance(anyTO, anyTypeClasses, anyFormLayout, 
pageRef);
+            }
+        } catch (NoSuchMethodException | SecurityException | 
InstantiationException | IllegalAccessException 
+                | IllegalArgumentException | InvocationTargetException e) {
             throw new IllegalArgumentException("Could not instantiate " + 
anyFormLayout.getFormClass().getName(), e);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/markup/html/LabelInfo.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/markup/html/LabelInfo.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/markup/html/LabelInfo.java
new file mode 100644
index 0000000..7857f0e
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/ajax/markup/html/LabelInfo.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.console.wicket.ajax.markup.html;
+
+import java.util.Collection;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.basic.Label;
+import org.springframework.util.CollectionUtils;
+
+public class LabelInfo extends Label {
+
+    private static final long serialVersionUID = 4755868673082976208L;
+
+    private final String title;
+
+    public LabelInfo(final String id, final String title) {
+        super(id, StringUtils.EMPTY);
+        this.title = title == null ? StringUtils.EMPTY : 
StringUtils.abbreviate(title, 30);
+    }
+
+    public LabelInfo(final String id, final Collection<String> title) {
+        super(id, StringUtils.EMPTY);
+        if (CollectionUtils.isEmpty(title)) {
+            this.title = StringUtils.EMPTY;
+        } else {
+            StringBuilder titleBuilder = new StringBuilder();
+            for (String el : title) {
+                if (titleBuilder.length() > 0) {
+                    titleBuilder.append("; ");
+                }
+                if (StringUtils.isNoneEmpty(el)) {
+                    titleBuilder.append(el);
+                }
+            }
+            this.title = StringUtils.abbreviate(titleBuilder.toString(), 50);
+        }
+    }
+
+    @Override
+    protected void onComponentTag(final ComponentTag tag) {
+        tag.put("class", "fa  fa-info-circle");
+        tag.put("style", "color:red");
+        tag.put("title", title);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractFieldPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractFieldPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractFieldPanel.java
index ba2263b..305a97b 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractFieldPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AbstractFieldPanel.java
@@ -21,7 +21,6 @@ package 
org.apache.syncope.client.console.wicket.markup.html.form;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.wicket.Component;
 import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.FormComponent;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
@@ -71,7 +70,7 @@ public abstract class AbstractFieldPanel<T> extends Panel {
         return this;
     }
 
-    public AbstractFieldPanel<T> showExternAction(final FormComponent<?> 
component) {
+    public AbstractFieldPanel<T> showExternAction(final Component component) {
         final Fragment fragment = new Fragment("externalAction", 
"externalActionFragment", AbstractFieldPanel.this);
         addOrReplace(fragment);
         fragment.add(component.setRenderBodyOnly(false));

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxSpinnerFieldPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxSpinnerFieldPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxSpinnerFieldPanel.java
index 12d315f..ae8729a 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxSpinnerFieldPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxSpinnerFieldPanel.java
@@ -25,6 +25,8 @@ import 
com.googlecode.wicket.jquery.ui.form.spinner.SpinnerAdapter;
 import com.googlecode.wicket.jquery.ui.form.spinner.SpinnerBehavior;
 import java.io.Serializable;
 import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.math.NumberUtils;
 import org.apache.syncope.client.console.commons.Constants;
 import 
org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
@@ -33,7 +35,6 @@ import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
-import org.springframework.util.StringUtils;
 
 public final class AjaxSpinnerFieldPanel<T extends Number> extends 
FieldPanel<T> {
 
@@ -97,7 +98,8 @@ public final class AjaxSpinnerFieldPanel<T extends Number> 
extends FieldPanel<T>
             public T getObject() {
                 T value = null;
 
-                if (list != null && !list.isEmpty() && 
StringUtils.hasText(list.get(0).toString())) {
+                if (CollectionUtils.isNotEmpty(list)
+                        && list.get(0) != null && 
StringUtils.isNotBlank(list.get(0).toString())) {
                     value = reference.equals(Integer.class)
                             ? 
reference.cast(NumberUtils.toInt(list.get(0).toString()))
                             : reference.equals(Long.class)

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 7d9687c..88a792c 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
@@ -74,7 +74,10 @@ public abstract class AbstractAttrs<S extends 
AbstractSchemaTO> extends WizardSt
 
     private final List<String> anyTypeClasses;
 
-    public AbstractAttrs(final AnyTO anyTO, final List<String> anyTypeClasses, 
final List<String> whichAttrs) {
+    public AbstractAttrs(
+            final AnyWrapper<?> modelObject,
+            final List<String> anyTypeClasses,
+            final List<String> whichAttrs) {
         super();
         this.anyTypeClasses = anyTypeClasses;
         this.attrTOs = new ListModel<>(Collections.<AttrTO>emptyList());
@@ -82,7 +85,7 @@ public abstract class AbstractAttrs<S extends 
AbstractSchemaTO> extends WizardSt
 
         this.setOutputMarkupId(true);
 
-        this.anyTO = anyTO;
+        this.anyTO = modelObject.getInnerObject();
         this.whichAttrs = whichAttrs;
     }
 
@@ -197,7 +200,7 @@ public abstract class AbstractAttrs<S extends 
AbstractSchemaTO> extends WizardSt
                 && 
org.apache.cxf.common.util.CollectionUtils.isEmpty(membershipTOs.getObject())) {
             response.render(OnDomReadyHeaderItem.forScript(
                     String.format("$('#emptyPlaceholder').append(\"%s\"); 
$('#attributes').hide();",
-                    getString("attribute.empty.list"))));
+                            getString("attribute.empty.list"))));
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 9f1d3d2..9b3bce6 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
@@ -100,17 +100,17 @@ public abstract class AnyWizardBuilder<A extends AnyTO> 
extends AjaxWizardBuilde
         }
 
         if (formLayoutInfo.isAuxClasses()) {
-            wizardModel.add(new AuxClasses(modelObject.getInnerObject(), 
anyTypeClasses));
+            wizardModel.add(new AuxClasses(modelObject, anyTypeClasses));
         }
 
         if (formLayoutInfo.isGroups()) {
-            wizardModel.add(new Groups(modelObject.getInnerObject(), mode == 
AjaxWizard.Mode.TEMPLATE));
+            wizardModel.add(new Groups(modelObject, mode == 
AjaxWizard.Mode.TEMPLATE));
         }
 
         // attributes panel steps
         if (formLayoutInfo.isPlainAttrs()) {
             wizardModel.add(new PlainAttrs(
-                    modelObject.getInnerObject(),
+                    modelObject,
                     null,
                     mode,
                     anyTypeClasses,
@@ -118,11 +118,11 @@ public abstract class AnyWizardBuilder<A extends AnyTO> 
extends AjaxWizardBuilde
         }
         if (formLayoutInfo.isDerAttrs() && mode != AjaxWizard.Mode.TEMPLATE) {
             wizardModel.add(new DerAttrs(
-                    modelObject.getInnerObject(), anyTypeClasses, 
formLayoutInfo.getWhichDerAttrs()));
+                    modelObject, anyTypeClasses, 
formLayoutInfo.getWhichDerAttrs()));
         }
         if (formLayoutInfo.isVirAttrs()) {
             wizardModel.add(new VirAttrs(
-                    modelObject.getInnerObject(), mode, anyTypeClasses, 
formLayoutInfo.getWhichVirAttrs()));
+                    modelObject, mode, anyTypeClasses, 
formLayoutInfo.getWhichVirAttrs()));
         }
 
         // role panel step (just available for users)
@@ -131,7 +131,7 @@ public abstract class AnyWizardBuilder<A extends AnyTO> 
extends AjaxWizardBuilde
                 && (formLayoutInfo instanceof UserFormLayoutInfo)
                 && UserFormLayoutInfo.class.cast(formLayoutInfo).isRoles()) {
 
-            wizardModel.add(new 
Roles(UserTO.class.cast(modelObject.getInnerObject())));
+            wizardModel.add(new Roles(modelObject));
         }
 
         // relationship panel step (available for users and any objects)
@@ -140,12 +140,12 @@ public abstract class AnyWizardBuilder<A extends AnyTO> 
extends AjaxWizardBuilde
                 || ((formLayoutInfo instanceof AnyObjectFormLayoutInfo)
                 && 
AnyObjectFormLayoutInfo.class.cast(formLayoutInfo).isRelationships())) {
 
-            wizardModel.add(new Relationships(modelObject.getInnerObject(), 
pageRef));
+            wizardModel.add(new Relationships(modelObject, pageRef));
         }
 
         // resource panel step
         if (formLayoutInfo.isResources()) {
-            wizardModel.add(new Resources(modelObject.getInnerObject()));
+            wizardModel.add(new Resources(modelObject));
         }
 
         return wizardModel;

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 5aa59aa..d0f8ac9 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
@@ -21,11 +21,15 @@ package org.apache.syncope.client.console.wizards.any;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.util.ListModel;
 
@@ -33,7 +37,7 @@ public class AuxClasses extends WizardStep {
 
     private static final long serialVersionUID = 552437609667518888L;
 
-    public <T extends AnyTO> AuxClasses(final T anyTO, final List<String> 
anyTypeClasses) {
+    public <T extends AnyTO> AuxClasses(final AnyWrapper<T> modelObject, final 
List<String> anyTypeClasses) {
         super();
         setOutputMarkupId(true);
 
@@ -47,7 +51,21 @@ public class AuxClasses extends WizardStep {
         }
         Collections.sort(choices);
         add(new 
AjaxPalettePanel.Builder<String>().setAllowOrder(true).build("auxClasses",
-                new PropertyModel<List<String>>(anyTO, "auxClasses"),
+                new PropertyModel<List<String>>(modelObject.getInnerObject(), 
"auxClasses"),
                 new ListModel<>(choices)).hideLabel().setOutputMarkupId(true));
+
+        // ------------------
+        // insert changed label if needed
+        // ------------------
+        if (modelObject instanceof UserWrapper
+                && UserWrapper.class.cast(modelObject).getPreviousUserTO() != 
null
+                && !ListUtils.isEqualList(
+                        modelObject.getInnerObject().getAuxClasses(),
+                        
UserWrapper.class.cast(modelObject).getPreviousUserTO().getAuxClasses())) {
+            add(new LabelInfo("changed", StringUtils.EMPTY));
+        } else {
+            add(new Label("changed", StringUtils.EMPTY));
+        }
+        // ------------------
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 012a662..fe6444d 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
@@ -49,11 +49,11 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
     private static final long serialVersionUID = -5387344116983102292L;
 
     public <T extends AnyTO> DerAttrs(
-            final T anyTO,
+            final AnyWrapper<T> modelObject,
             final List<String> anyTypeClasses,
             final List<String> whichDerAttrs) {
 
-        super(anyTO, anyTypeClasses, whichDerAttrs);
+        super(modelObject, anyTypeClasses, whichDerAttrs);
         setTitleModel(new ResourceModel("attributes.derived"));
 
         add(new Accordion("derSchemas", Collections.<ITab>singletonList(new 
AbstractTab(

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
index 6525be5..a348f94 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
@@ -20,8 +20,10 @@ package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.StringUtils;
@@ -29,6 +31,7 @@ import 
org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
 import org.apache.syncope.client.console.SyncopeConsoleApplication;
 import org.apache.syncope.client.console.rest.DynRealmRestClient;
 import org.apache.syncope.client.console.rest.GroupRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.EntityTOUtils;
@@ -64,11 +67,11 @@ public class Groups extends WizardStep implements 
ICondition {
 
     private final AnyTO anyTO;
 
-    public <T extends AnyTO> Groups(final T anyTO, final boolean templateMode) 
{
+    public <T extends AnyTO> Groups(final AnyWrapper<T> modelObject, final 
boolean templateMode) {
         super();
-        this.anyTO = anyTO;
 
-        final String realm = templateMode ? "/" : anyTO.getRealm();
+        this.anyTO = modelObject.getInnerObject();
+        final String realm = templateMode ? "/" : this.anyTO.getRealm();
 
         // -----------------------------------------------------------------
         // Pre-Authorizations
@@ -136,6 +139,33 @@ public class Groups extends WizardStep implements 
ICondition {
             // ---------------------------------
             // Retrieve group memberships
             // ---------------------------------
+            // this is to be sure to have group names (required to see 
membership details in approval page)
+            GroupFiqlSearchConditionBuilder searchConditionBuilder = 
SyncopeClient.getGroupSearchConditionBuilder();
+
+            List<CompleteCondition> conditions = new ArrayList<>();
+            for (MembershipTO membershipTO : 
GroupableRelatableTO.class.cast(anyTO).getMemberships()) {
+                
conditions.add(searchConditionBuilder.is("key").equalTo(membershipTO.getGroupKey()).wrap());
+            }
+
+            List<GroupTO> groups = new ArrayList<>();
+            if (!conditions.isEmpty()) {
+                groups.addAll(groupRestClient.search(
+                        "/",
+                        searchConditionBuilder.or(conditions).query(),
+                        -1,
+                        -1,
+                        new SortParam<>("name", true),
+                        null));
+            }
+
+            // set group names in membership TOs
+            Map<String, MembershipTO> membershipMap = 
GroupableRelatableTO.class.cast(anyTO).getMembershipMap();
+            for (GroupTO group : groups) {
+                if (membershipMap.containsKey(group.getKey())) {
+                    
membershipMap.get(group.getKey()).setGroupName(group.getName());
+                }
+            }
+
             
groupsContainer.add(builder.setAllowOrder(true).withFilter().build("groups",
                     new 
ListModel<>(GroupableRelatableTO.class.cast(anyTO).getMemberships()),
                     new AjaxPalettePanel.Builder.Query<MembershipTO>() {
@@ -150,7 +180,7 @@ public class Groups extends WizardStep implements 
ICondition {
                             : groupRestClient.search(
                                     realm,
                                     
SyncopeClient.getGroupSearchConditionBuilder().
-                                            
isAssignable().and().is("name").equalTo(filter).query(),
+                                    
isAssignable().and().is("name").equalTo(filter).query(),
                                     1, MAX_GROUP_LIST_CARDINALITY,
                                     new SortParam<>("name", true),
                                     null),
@@ -170,16 +200,16 @@ public class Groups extends WizardStep implements 
ICondition {
             // ---------------------------------
             // Retrieve dyn group memberships
             // ---------------------------------
-            GroupFiqlSearchConditionBuilder searchConditionBuilder = 
SyncopeClient.getGroupSearchConditionBuilder();
+            searchConditionBuilder = 
SyncopeClient.getGroupSearchConditionBuilder();
 
-            List<CompleteCondition> conditions = new ArrayList<>();
+            conditions = new ArrayList<>();
             for (String groupKey : 
GroupableRelatableTO.class.cast(anyTO).getDynGroups()) {
                 
conditions.add(searchConditionBuilder.is("key").equalTo(groupKey).wrap());
             }
 
-            List<GroupTO> dynGroups = new ArrayList<>();
+            groups = new ArrayList<>();
             if (!conditions.isEmpty()) {
-                dynGroups.addAll(groupRestClient.search(
+                groups.addAll(groupRestClient.search(
                         "/",
                         searchConditionBuilder.or(conditions).query(),
                         -1,
@@ -189,7 +219,7 @@ public class Groups extends WizardStep implements 
ICondition {
             }
 
             dyngroupsContainer.add(new 
AjaxPalettePanel.Builder<String>().setAllowOrder(true).build("dyngroups",
-                    new ListModel<>(CollectionUtils.collect(dynGroups, new 
Transformer<GroupTO, String>() {
+                    new ListModel<>(CollectionUtils.collect(groups, new 
Transformer<GroupTO, String>() {
 
                         @Override
                         public String transform(final GroupTO input) {
@@ -215,6 +245,20 @@ public class Groups extends WizardStep implements 
ICondition {
                                 EntityTOUtils.keyTransformer(),
                                 new ArrayList<String>()))).
                 hideLabel().setEnabled(false).setOutputMarkupId(true));
+
+        // ------------------
+        // insert changed label if needed
+        // ------------------
+        if (modelObject instanceof UserWrapper
+                && UserWrapper.class.cast(modelObject).getPreviousUserTO() != 
null
+                && !ListUtils.isEqualList(
+                        
UserWrapper.class.cast(modelObject).getInnerObject().getMemberships(),
+                        
UserWrapper.class.cast(modelObject).getPreviousUserTO().getMemberships())) {
+            groupsContainer.add(new LabelInfo("changed", StringUtils.EMPTY));
+        } else {
+            groupsContainer.add(new Label("changed", StringUtils.EMPTY));
+        }
+        // ------------------
     }
 
     @Override
@@ -223,6 +267,6 @@ public class Groups extends WizardStep implements 
ICondition {
                 ? CollectionUtils.isNotEmpty(allDynRealms)
                 : CollectionUtils.isNotEmpty(allDynRealms) || 
CollectionUtils.isNotEmpty(allGroups))
                 && 
SyncopeConsoleApplication.get().getSecuritySettings().getAuthorizationStrategy().
-                        isActionAuthorized(this, RENDER);
+                isActionAuthorized(this, RENDER);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 80891cf..75ca3c4 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
@@ -23,9 +23,13 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.SchemaUtils;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
+import 
org.apache.syncope.client.console.wicket.markup.html.form.AbstractFieldPanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxSpinnerFieldPanel;
@@ -65,16 +69,24 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
 
     private final AjaxWizard.Mode mode;
 
+    private final AnyTO previousObject;
+
     public <T extends AnyTO> PlainAttrs(
-            final T anyTO,
+            final AnyWrapper<T> modelObject,
             final Form<?> form,
             final AjaxWizard.Mode mode,
             final List<String> anyTypeClasses,
             final List<String> whichPlainAttrs) throws 
IllegalArgumentException {
 
-        super(anyTO, anyTypeClasses, whichPlainAttrs);
+        super(modelObject, anyTypeClasses, whichPlainAttrs);
         this.mode = mode;
 
+        if (modelObject instanceof UserWrapper) {
+            previousObject = 
UserWrapper.class.cast(modelObject).getPreviousUserTO();
+        } else {
+            previousObject = null;
+        }
+
         setTitleModel(new ResourceModel("attributes.plain"));
 
         add(new Accordion("plainSchemas", Collections.<ITab>singletonList(new 
AbstractTab(
@@ -337,17 +349,41 @@ public class PlainAttrs extends 
AbstractAttrs<PlainSchemaTO> {
                 protected void populateItem(final ListItem<AttrTO> item) {
                     AttrTO attrTO = item.getModelObject();
 
-                    FieldPanel panel = 
getFieldPanel(availableSchemas.get(attrTO.getSchema()));
+                    AbstractFieldPanel<?> panel = 
getFieldPanel(availableSchemas.get(attrTO.getSchema()));
                     if (mode == AjaxWizard.Mode.TEMPLATE
                             || 
!availableSchemas.get(attrTO.getSchema()).isMultivalue()) {
-                        panel.setNewModel(attrTO.getValues());
-                        item.add(panel);
+                        
FieldPanel.class.cast(panel).setNewModel(attrTO.getValues());
                     } else {
-                        item.add(new MultiFieldPanel.Builder<>(
+                        panel = new MultiFieldPanel.Builder<>(
                                 new PropertyModel<List<String>>(attrTO, 
"values")).build(
                                 "panel",
                                 attrTO.getSchema(),
-                                panel));
+                                FieldPanel.class.cast(panel));
+                    }
+                    item.add(panel);
+
+                    if (previousObject != null
+                            && 
(previousObject.getPlainAttr(attrTO.getSchema()) == null
+                            || !ListUtils.isEqualList(
+                                    
ListUtils.select(previousObject.getPlainAttr(attrTO.getSchema()).getValues(),
+                                            new Predicate<String>() {
+
+                                        @Override
+                                        public boolean evaluate(final String 
object) {
+                                            return 
StringUtils.isNotEmpty(object);
+                                        }
+                                    }), ListUtils.select(attrTO.getValues(),
+                                            new Predicate<String>() {
+
+                                        @Override
+                                        public boolean evaluate(final String 
object) {
+                                            return 
StringUtils.isNotEmpty(object);
+                                        }
+                                    })))) {
+                        List<String> oldValues = 
previousObject.getPlainAttr(attrTO.getSchema()) == null
+                                ? Collections.<String>emptyList()
+                                : 
previousObject.getPlainAttr(attrTO.getSchema()).getValues();
+                        panel.showExternAction(new LabelInfo("externalAction", 
oldValues));
                     }
                 }
             });

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
index 0d2c323..c476251 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
@@ -28,6 +28,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.SerializableTransformer;
 import org.apache.syncope.client.console.panels.AnyDirectoryPanel;
@@ -43,6 +44,7 @@ import 
org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.rest.RelationshipTypeRestClient;
 import 
org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
@@ -59,16 +61,19 @@ import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.RelationshipTypeTO;
 import org.apache.syncope.common.lib.types.AnyEntitlement;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.Component;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.event.IEvent;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.extensions.wizard.IWizard;
 import org.apache.wicket.extensions.wizard.WizardStep;
 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.basic.Label;
 import org.apache.wicket.markup.html.form.IChoiceRenderer;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -89,10 +94,21 @@ public class Relationships extends WizardStep {
 
     private final AnyTO anyTO;
 
-    public Relationships(final AnyTO anyTO, final PageReference pageRef) {
+    public Relationships(final AnyWrapper<?> modelObject, final PageReference 
pageRef) {
         super();
-        setTitleModel(new ResourceModel("any.relationships"));
-        this.anyTO = anyTO;
+        add(new Label("title", new ResourceModel("any.relationships")));
+
+        if (modelObject instanceof UserWrapper
+                && UserWrapper.class.cast(modelObject).getPreviousUserTO() != 
null
+                && !ListUtils.isEqualList(
+                        
UserWrapper.class.cast(modelObject).getInnerObject().getRelationships(),
+                        
UserWrapper.class.cast(modelObject).getPreviousUserTO().getRelationships())) {
+            add(new LabelInfo("changed", StringUtils.EMPTY));
+        } else {
+            add(new Label("changed", StringUtils.EMPTY));
+        }
+
+        this.anyTO = modelObject.getInnerObject();
         this.pageRef = pageRef;
 
         // ------------------------
@@ -102,6 +118,11 @@ public class Relationships extends WizardStep {
         // ------------------------ 
     }
 
+    @Override
+    public Component getHeader(final String id, final Component parent, final 
IWizard wizard) {
+        return super.getHeader(id, parent, wizard).setVisible(false);
+    }
+
     private Fragment getViewFragment() {
         final Map<String, List<RelationshipTO>> relationships = new 
HashMap<>();
         addRelationship(relationships, getCurrentRelationships().toArray(new 
RelationshipTO[] {}));

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java
index fbe729d..2ace1c7 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/Resources.java
@@ -22,8 +22,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleApplication;
 import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -33,6 +36,7 @@ import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.Action
 import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
 import org.apache.wicket.extensions.wizard.WizardModel;
 import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.util.ListModel;
 
@@ -42,7 +46,19 @@ public class Resources extends WizardStep implements 
WizardModel.ICondition {
 
     private final ListModel<String> available;
 
-    public <T extends AnyTO> Resources(final T entityTO) {
+    public <T extends AnyTO> Resources(final AnyWrapper<T> modelObject) {
+        final T entityTO = modelObject.getInnerObject();
+
+        if (modelObject instanceof UserWrapper
+                && UserWrapper.class.cast(modelObject).getPreviousUserTO() != 
null
+                && !ListUtils.isEqualList(
+                        modelObject.getInnerObject().getResources(),
+                        
UserWrapper.class.cast(modelObject).getPreviousUserTO().getResources())) {
+            add(new LabelInfo("changed", StringUtils.EMPTY));
+        } else {
+            add(new Label("changed", StringUtils.EMPTY));
+        }
+
         // -----------------------------------------------------------------
         // Pre-Authorizations
         // -----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 6e44582..07d8275 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
@@ -22,8 +22,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.ListUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleApplication;
 import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -34,6 +37,7 @@ import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.Action
 import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
 import org.apache.wicket.extensions.wizard.WizardModel.ICondition;
 import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.model.util.ListModel;
 
@@ -43,7 +47,22 @@ public class Roles extends WizardStep implements ICondition {
 
     private final List<String> allRoles;
 
-    public <T extends AnyTO> Roles(final UserTO entityTO) {
+    public <T extends AnyTO> Roles(final AnyWrapper<?> modelObject) {
+        if (!(modelObject.getInnerObject() instanceof UserTO)) {
+            throw new IllegalStateException("Invalid instance " + 
modelObject.getInnerObject());
+        }
+
+        if (UserWrapper.class.cast(modelObject).getPreviousUserTO() != null
+                && !ListUtils.isEqualList(
+                        
UserWrapper.class.cast(modelObject).getInnerObject().getRoles(),
+                        
UserWrapper.class.cast(modelObject).getPreviousUserTO().getRoles())) {
+            add(new LabelInfo("changed", StringUtils.EMPTY));
+        } else {
+            add(new Label("changed", StringUtils.EMPTY));
+        }
+
+        final UserTO entityTO = 
UserTO.class.cast(modelObject.getInnerObject());
+
         // -----------------------------------------------------------------
         // Pre-Authorizations
         // -----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 60e3c4d..844ff4b 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
@@ -19,6 +19,8 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.Collections;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.common.lib.to.UserTO;
@@ -56,6 +58,11 @@ public class UserDetails extends Details<UserTO> {
         final AjaxTextFieldPanel username = new AjaxTextFieldPanel(
                 "username", "username", new PropertyModel<String>(userTO, 
"username"), false);
 
+        if (wrapper.getPreviousUserTO() != null && StringUtils.
+                compare(wrapper.getPreviousUserTO().getUsername(), 
wrapper.getInnerObject().getUsername()) != 0) {
+            username.showExternAction(new LabelInfo("externalAction", 
wrapper.getPreviousUserTO().getUsername()));
+        }
+
         if (templateMode) {
             username.enableJexlHelp();
         } else {

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java
index f2f1477..8d0dc5b 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserTemplateWizardBuilder.java
@@ -38,7 +38,7 @@ public class UserTemplateWizardBuilder extends 
UserWizardBuilder implements Temp
             final List<String> anyTypeClasses,
             final UserFormLayoutInfo formLayoutInfo,
             final PageReference pageRef) {
-        super(null, anyTypeClasses, formLayoutInfo, pageRef);
+        super(anyTypeClasses, formLayoutInfo, pageRef);
         this.templatable = templatable;
 
         if (templatable.getTemplates().containsKey(AnyTypeKind.USER.name())) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 d2afa25..f56e24d 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
@@ -38,13 +38,38 @@ public class UserWizardBuilder extends 
AnyWizardBuilder<UserTO> implements UserF
 
     private final UserRestClient userRestClient = new UserRestClient();
 
+    /**
+     * Costructor to be used for templating only.
+     *
+     * @param anyTypeClasses any type classes.
+     * @param formLayoutInfo form layout.
+     * @param pageRef reference page.
+     */
+    public UserWizardBuilder(
+            final List<String> anyTypeClasses,
+            final UserFormLayoutInfo formLayoutInfo,
+            final PageReference pageRef) {
+
+        super(new UserWrapper(null), anyTypeClasses, formLayoutInfo, pageRef);
+    }
+
+    /**
+     * Constructor to be used for Approval details only.
+     *
+     * @param previousUserTO previous user status.
+     * @param userTO new user status to be approved.
+     * @param anyTypeClasses any type classes.
+     * @param formLayoutInfo from layout.
+     * @param pageRef reference page.
+     */
     public UserWizardBuilder(
+            final UserTO previousUserTO,
             final UserTO userTO,
             final List<String> anyTypeClasses,
             final UserFormLayoutInfo formLayoutInfo,
             final PageReference pageRef) {
 
-        super(new UserWrapper(userTO), anyTypeClasses, formLayoutInfo, 
pageRef);
+        super(new UserWrapper(previousUserTO, userTO), anyTypeClasses, 
formLayoutInfo, pageRef);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWrapper.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWrapper.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWrapper.java
index c2a04d9..2476c63 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWrapper.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWrapper.java
@@ -26,8 +26,15 @@ public class UserWrapper extends AnyWrapper<UserTO> {
 
     private boolean storePasswordInSyncope = true;
 
+    private UserTO previousUserTO;
+
     public UserWrapper(final UserTO userTO) {
+        this(null, userTO);
+    }
+
+    public UserWrapper(final UserTO previousUserTO, final UserTO userTO) {
         super(userTO);
+        this.previousUserTO = previousUserTO;
     }
 
     public boolean isStorePasswordInSyncope() {
@@ -38,4 +45,7 @@ public class UserWrapper extends AnyWrapper<UserTO> {
         this.storePasswordInSyncope = storePasswordInSyncope;
     }
 
+    public UserTO getPreviousUserTO() {
+        return previousUserTO;
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/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 4b66f0d..4ab5bee 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
@@ -22,8 +22,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
+import 
org.apache.syncope.client.console.wicket.markup.html.form.AbstractFieldPanel;
 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.client.console.wizards.AjaxWizard;
@@ -51,14 +54,17 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
 
     private final AjaxWizard.Mode mode;
 
+    private final AnyWrapper<?> modelObject;
+
     public <T extends AnyTO> VirAttrs(
-            final T anyTO,
+            final AnyWrapper<T> modelObject,
             final AjaxWizard.Mode mode,
             final List<String> anyTypeClasses,
             final List<String> whichVirAttrs) {
 
-        super(anyTO, anyTypeClasses, whichVirAttrs);
+        super(modelObject, anyTypeClasses, whichVirAttrs);
         this.mode = mode;
+        this.modelObject = modelObject;
 
         setTitleModel(new ResourceModel("attributes.virtual"));
 
@@ -174,24 +180,34 @@ public class VirAttrs extends AbstractAttrs<VirSchemaTO> {
                 protected void populateItem(final ListItem<AttrTO> item) {
                     AttrTO attrTO = item.getModelObject();
 
-                    AjaxTextFieldPanel panel =
-                            new AjaxTextFieldPanel("panel", 
attrTO.getSchema(), new Model<String>(), false);
+                    AbstractFieldPanel<?> panel
+                            = new AjaxTextFieldPanel("panel", 
attrTO.getSchema(), new Model<String>(), false);
 
                     boolean readonly = attrTO.getSchemaInfo() == null
                             ? false
                             : 
VirSchemaTO.class.cast(attrTO.getSchemaInfo()).isReadonly();
 
                     if (mode == AjaxWizard.Mode.TEMPLATE) {
-                        item.add(panel.enableJexlHelp().setEnabled(!readonly));
+                        
AjaxTextFieldPanel.class.cast(panel).enableJexlHelp().setEnabled(!readonly);
                     } else {
-                        item.add(new MultiFieldPanel.Builder<>(
+                        panel = new MultiFieldPanel.Builder<>(
                                 new PropertyModel<List<String>>(attrTO, 
"values")).build(
                                 "panel",
                                 attrTO.getSchema(),
-                                panel).setEnabled(!readonly));
+                                AjaxTextFieldPanel.class.cast(panel));
+                        panel.setEnabled(!readonly);
+                    }
+
+                    item.add(panel);
+
+                    if (CollectionUtils.isNotEmpty(attrTO.getValues())
+                            && VirAttrs.this.modelObject instanceof UserWrapper
+                            && 
UserWrapper.class.cast(VirAttrs.this.modelObject).getPreviousUserTO() != null) {
+                        panel.showExternAction(new LabelInfo("externalAction", 
StringUtils.EMPTY));
                     }
                 }
-            });
+            }
+            );
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/AuxClasses.html
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/AuxClasses.html
 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/AuxClasses.html
index 851d51b..f70fdfe 100644
--- 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/AuxClasses.html
+++ 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/AuxClasses.html
@@ -24,6 +24,7 @@ under the License.
           <h3 class="box-title">
             <wicket:message key="auxClasses.palette">AUX 
CLASSES</wicket:message>
           </h3>
+          <span wicket:id="changed"></span>
         </div>
         <div class="box-body">
           <span wicket:id="auxClasses">[AUX CLASSES]</span>

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Groups.html
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Groups.html
 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Groups.html
index 3a5b769..10b6524 100644
--- 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Groups.html
+++ 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Groups.html
@@ -24,6 +24,7 @@ under the License.
           <h3 class="box-title">
             <wicket:message key="groups.palette">[GROUPS]</wicket:message>
           </h3>
+          <span wicket:id="changed"></span>
         </div>
         <div class="box-body">
           <span wicket:id="groups"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Relationships.html
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Relationships.html
 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Relationships.html
index 34e8266..bfa6f45 100644
--- 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Relationships.html
+++ 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Relationships.html
@@ -20,6 +20,10 @@ under the License.
   <head><title></title></head>
   <body>
     <wicket:panel>
+      <div class="wizard-step-title">
+        <div><span wicket:id="title">[RELATIONSHIPS]</span></div><span 
wicket:id="changed"></span>
+      </div>
+
       <div id="emptyPlaceholder"/>
       <span wicket:id="relationships"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Resources.html
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Resources.html
 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Resources.html
index ae54159..54219d1 100644
--- 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Resources.html
+++ 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Resources.html
@@ -26,6 +26,7 @@ under the License.
             <h3 class="box-title">
               <wicket:message key="resources.palette">[ROLES]</wicket:message>
             </h3>
+            <span wicket:id="changed"></span>
           </div>
           <div class="box-body">
             <span wicket:id="resources">[RESOURCES]</span>

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Roles.html
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Roles.html
 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Roles.html
index 65c8b50..f7eded7 100644
--- 
a/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Roles.html
+++ 
b/client/console/src/main/resources/org/apache/syncope/client/console/wizards/any/Roles.html
@@ -26,13 +26,14 @@ under the License.
             <h3 class="box-title">
               <wicket:message key="roles.palette">[ROLES]</wicket:message>
             </h3>
+            <span wicket:id="changed"></span>
           </div>
           <div class="box-body">
             <span wicket:id="roles">[ROLES]</span>
           </div>
         </div>
       </div>
-      
+
       <div class="col-xs-12">
         <div class="box">
           <div class="box-header">

http://git-wip-us.apache.org/repos/asf/syncope/blob/78b68bf4/common/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
----------------------------------------------------------------------
diff --git 
a/common/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java 
b/common/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
index 69d373e..0008842 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/AnyOperations.java
@@ -438,17 +438,6 @@ public final class AnyOperations {
                 if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
                     rwattrs.put(patch.getAttrTO().getSchema(), 
patch.getAttrTO());
                 }
-                switch (patch.getOperation()) {
-                    case ADD_REPLACE:
-                        if 
(rwattrs.containsKey(patch.getAttrTO().getSchema())) {
-                            rwattrs.remove(patch.getAttrTO().getSchema());
-                        }
-                        break;
-
-                    case DELETE:
-                    default:
-                        rwattrs.remove(patch.getAttrTO().getSchema());
-                }
             }
         }
 
@@ -472,12 +461,12 @@ public final class AnyOperations {
         for (StringPatchItem auxClassPatch : patch.getAuxClasses()) {
             switch (auxClassPatch.getOperation()) {
                 case ADD_REPLACE:
-                    to.getAuxClasses().add(auxClassPatch.getValue());
+                    result.getAuxClasses().add(auxClassPatch.getValue());
                     break;
 
                 case DELETE:
                 default:
-                    to.getAuxClasses().remove(auxClassPatch.getValue());
+                    result.getAuxClasses().remove(auxClassPatch.getValue());
             }
         }
 

Reply via email to