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

[3/8] syncope git commit: [SYNCOPE-1299] Console features implemented

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
new file mode 100644
index 0000000..1319a5b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
@@ -0,0 +1,155 @@
+/*
+ * 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.status;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.rest.ReconciliationRestClient;
+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.AjaxPalettePanel;
+import org.apache.syncope.common.lib.to.ProvisioningTaskTO;
+import org.apache.syncope.common.lib.to.PullTaskTO;
+import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.util.ListModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
+
+    private static final long serialVersionUID = 5870444905957760434L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(ReconTaskPanel.class);
+
+    private final ReconciliationRestClient restClient = new ReconciliationRestClient();
+
+    private final IModel<List<String>> pullActionsClasses = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return new ArrayList<>(SyncopeConsoleSession.get().getPlatformInfo().getPullActions());
+        }
+    };
+
+    private final IModel<List<String>> pushActionsClasses = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return new ArrayList<>(SyncopeConsoleSession.get().getPlatformInfo().getPushActions());
+        }
+    };
+
+    public ReconTaskPanel(
+            final String resource,
+            final ProvisioningTaskTO taskTO,
+            final AnyTypeKind anyTypeKind,
+            final String anyKey,
+            final MultilevelPanel multiLevelPanelRef,
+            final PageReference pageRef) {
+
+        Form<ProvisioningTaskTO> form = new Form<>("form", new CompoundPropertyModel<>(taskTO));
+        add(form);
+
+        AjaxPalettePanel<String> actionsClassNames = new AjaxPalettePanel.Builder<String>().
+                setAllowMoveAll(true).setAllowOrder(true).
+                build("actionsClassNames",
+                        new PropertyModel<List<String>>(taskTO, "actionsClassNames"),
+                        new ListModel<>(taskTO instanceof PushTaskTO
+                                ? pushActionsClasses.getObject() : pullActionsClasses.getObject()));
+        actionsClassNames.setOutputMarkupId(true);
+        form.add(actionsClassNames);
+
+        AjaxDropDownChoicePanel<MatchingRule> matchingRule = new AjaxDropDownChoicePanel<>(
+                "matchingRule", "matchingRule", new PropertyModel<MatchingRule>(taskTO, "matchingRule"), false);
+        matchingRule.setChoices(Arrays.asList(MatchingRule.values()));
+        form.add(matchingRule);
+
+        AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
+                "unmatchingRule", "unmatchingRule", new PropertyModel<UnmatchingRule>(taskTO, "unmatchingRule"),
+                false);
+        unmatchingRule.setChoices(Arrays.asList(UnmatchingRule.values()));
+        form.add(unmatchingRule);
+
+        taskTO.setPerformCreate(true);
+        AjaxCheckBoxPanel performCreate = new AjaxCheckBoxPanel(
+                "performCreate", "performCreate", new PropertyModel<Boolean>(taskTO, "performCreate"), false);
+        form.add(performCreate);
+
+        taskTO.setPerformUpdate(true);
+        AjaxCheckBoxPanel performUpdate = new AjaxCheckBoxPanel(
+                "performUpdate", "performUpdate", new PropertyModel<Boolean>(taskTO, "performUpdate"), false);
+        form.add(performUpdate);
+
+        taskTO.setPerformDelete(true);
+        AjaxCheckBoxPanel performDelete = new AjaxCheckBoxPanel(
+                "performDelete", "performDelete", new PropertyModel<Boolean>(taskTO, "performDelete"), false);
+        form.add(performDelete);
+
+        taskTO.setSyncStatus(true);
+        AjaxCheckBoxPanel syncStatus = new AjaxCheckBoxPanel(
+                "syncStatus", "syncStatus", new PropertyModel<Boolean>(taskTO, "syncStatus"), false);
+        form.add(syncStatus);
+
+        form.add(new AjaxSubmitLink("reconcile") {
+
+            private static final long serialVersionUID = -817438685948164787L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                try {
+                    if (taskTO instanceof PushTaskTO) {
+                        restClient.push(anyTypeKind, anyKey, resource, (PushTaskTO) form.getModelObject());
+                    } else {
+                        restClient.pull(anyTypeKind, anyKey, resource, (PullTaskTO) form.getModelObject());
+                    }
+
+                    SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                } catch (Exception e) {
+                    LOG.error("While attempting reconciliation on {} {} {} {}",
+                            anyTypeKind, anyKey, resource, form.getModelObject(), e);
+                    SyncopeConsoleSession.get().error(resource + ": "
+                            + (StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage()));
+                }
+                multiLevelPanelRef.prev(target);
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java
index 1431442..4902f15 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusDirectoryPanel.java
@@ -21,9 +21,11 @@ package org.apache.syncope.client.console.status;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.DirectoryDataProvider;
-import org.apache.syncope.client.console.commons.ResourceStatusDataProvider;
+import org.apache.syncope.client.console.commons.status.AbstractStatusBeanProvider;
 import org.apache.syncope.client.console.commons.status.StatusBean;
 import org.apache.syncope.client.console.commons.status.StatusUtils;
 import org.apache.syncope.client.console.panels.DirectoryPanel;
@@ -36,16 +38,21 @@ import org.apache.syncope.client.console.rest.GroupRestClient;
 import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PullTaskTO;
+import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
 import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.StringResourceModel;
 
@@ -61,21 +68,21 @@ public class ResourceStatusDirectoryPanel
 
     private String type;
 
-    private final ResourceTO resourceTO;
+    private final ResourceTO resource;
 
     public ResourceStatusDirectoryPanel(
             final BaseModal<?> baseModal,
             final MultilevelPanel multiLevelPanelRef,
             final PageReference pageRef,
             final String type,
-            final ResourceTO resourceTO) {
+            final ResourceTO resource) {
 
         super(MultilevelPanel.FIRST_LEVEL_ID, pageRef);
         this.baseModal = baseModal;
         this.multiLevelPanelRef = multiLevelPanelRef;
         this.type = type;
-        this.resourceTO = resourceTO;
-        this.itemKeyFieldName = "key";
+        this.resource = resource;
+        this.itemKeyFieldName = "name";
 
         initResultTable();
     }
@@ -90,43 +97,117 @@ public class ResourceStatusDirectoryPanel
         final List<IColumn<StatusBean, String>> columns = new ArrayList<>();
 
         columns.add(new PropertyColumn<StatusBean, String>(
-                new StringResourceModel("key", this), "key", "key"));
+                new StringResourceModel("name", this), "name", "name"));
 
-        columns.add(new PropertyColumn<StatusBean, String>(
-                new StringResourceModel("connObjectLink", this), "connObjectLink", "connObjectLink"));
+        return columns;
+    }
+
+    private AnyTypeKind getAnyTypeKind() {
+        if (StringUtils.isBlank(type)) {
+            return null;
+        }
+
+        switch (type) {
+            case "USER":
+                return AnyTypeKind.USER;
+
+            case "GROUP":
+                return AnyTypeKind.GROUP;
 
-        columns.add(new AbstractColumn<StatusBean, String>(new StringResourceModel("status", this)) {
+            default:
+                return AnyTypeKind.ANY_OBJECT;
+        }
+    }
+
+    @Override
+    public ActionsPanel<StatusBean> getActions(final IModel<StatusBean> model) {
+        final ActionsPanel<StatusBean> panel = super.getActions(model);
 
-            private static final long serialVersionUID = -3503023501954863131L;
+        panel.add(new ActionLink<StatusBean>() {
+
+            private static final long serialVersionUID = -7978723352517770645L;
 
             @Override
-            public void populateItem(
-                    final Item<ICellPopulator<StatusBean>> cellItem,
-                    final String componentId,
-                    final IModel<StatusBean> model) {
-
-                if (model.getObject().isLinked()) {
-                    cellItem.add(StatusUtils.getStatusImage(componentId, model.getObject().getStatus()));
-                } else {
-                    cellItem.add(new Label(componentId, ""));
-                }
+            protected boolean statusCondition(final StatusBean bean) {
+                return getAnyTypeKind() != null;
             }
-        });
 
-        return columns;
+            @Override
+            public void onClick(final AjaxRequestTarget target, final StatusBean bean) {
+                multiLevelPanelRef.next(bean.getResource(),
+                        new ReconStatusPanel(bean.getResource(), getAnyTypeKind(), bean.getKey()),
+                        target);
+                target.add(multiLevelPanelRef);
+                getTogglePanel().close(target);
+            }
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.RESOURCE_GET_CONNOBJECT);
+
+        panel.add(new ActionLink<StatusBean>() {
+
+            private static final long serialVersionUID = -7978723352517770645L;
+
+            @Override
+            protected boolean statusCondition(final StatusBean bean) {
+                return getAnyTypeKind() != null;
+            }
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final StatusBean bean) {
+                multiLevelPanelRef.next("PUSH " + bean.getResource(),
+                        new ReconTaskPanel(
+                                bean.getResource(),
+                                new PushTaskTO(),
+                                getAnyTypeKind(),
+                                bean.getKey(),
+                                multiLevelPanelRef,
+                                pageRef),
+                        target);
+                target.add(multiLevelPanelRef);
+                getTogglePanel().close(target);
+            }
+        }, ActionLink.ActionType.RECONCILIATION_PUSH, StandardEntitlement.TASK_EXECUTE);
+
+        panel.add(new ActionLink<StatusBean>() {
+
+            private static final long serialVersionUID = -7978723352517770645L;
+
+            @Override
+            protected boolean statusCondition(final StatusBean bean) {
+                return getAnyTypeKind() != null;
+            }
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final StatusBean bean) {
+                multiLevelPanelRef.next("PULL " + bean.getResource(),
+                        new ReconTaskPanel(
+                                bean.getResource(),
+                                new PullTaskTO(),
+                                getAnyTypeKind(),
+                                bean.getKey(),
+                                multiLevelPanelRef,
+                                pageRef),
+                        target);
+                target.add(multiLevelPanelRef);
+                getTogglePanel().close(target);
+            }
+        }, ActionLink.ActionType.RECONCILIATION_PULL, StandardEntitlement.TASK_EXECUTE);
+
+        return panel;
     }
 
     public void updateResultTable(final String type, final AjaxRequestTarget target) {
         this.type = type;
 
-        if (StringUtils.isNoneEmpty(type)) {
+        if (StringUtils.isNotBlank(type)) {
             switch (type) {
                 case "USER":
                     this.restClient = new UserRestClient();
                     break;
+
                 case "GROUP":
                     this.restClient = new GroupRestClient();
                     break;
+
                 default:
                     this.restClient = new AnyObjectRestClient();
             }
@@ -137,20 +218,91 @@ public class ResourceStatusDirectoryPanel
 
     @Override
     protected Collection<ActionLink.ActionType> getBulkActions() {
-        final List<ActionLink.ActionType> bulkActions = new ArrayList<>();
+        List<ActionLink.ActionType> bulkActions = new ArrayList<>();
         bulkActions.add(ActionLink.ActionType.UNLINK);
+        bulkActions.add(ActionLink.ActionType.LINK);
         bulkActions.add(ActionLink.ActionType.DEPROVISION);
+        bulkActions.add(ActionLink.ActionType.PROVISION);
+        bulkActions.add(ActionLink.ActionType.ASSIGN);
         bulkActions.add(ActionLink.ActionType.UNASSIGN);
         return bulkActions;
     }
 
     @Override
     protected ResourceStatusDataProvider dataProvider() {
-        return new ResourceStatusDataProvider(type, resourceTO.getKey(), rows, SyncopeConstants.ROOT_REALM);
+        return new ResourceStatusDataProvider();
     }
 
     @Override
     protected String paginatorRowsKey() {
         return StringUtils.EMPTY;
     }
+
+    protected class ResourceStatusDataProvider extends AbstractStatusBeanProvider {
+
+        private static final long serialVersionUID = 4586969457669796621L;
+
+        private final String fiql;
+
+        private final AbstractAnyRestClient<? extends AnyTO> restClient;
+
+        public ResourceStatusDataProvider() {
+            super(AnyTypeKind.USER.name().equals(type) ? "username" : "name");
+
+            if (StringUtils.isEmpty(type)) {
+                fiql = null;
+                restClient = null;
+            } else {
+                AbstractFiqlSearchConditionBuilder bld;
+                switch (type) {
+                    case "USER":
+                        bld = SyncopeClient.getUserSearchConditionBuilder();
+                        restClient = new UserRestClient();
+                        break;
+
+                    case "GROUP":
+                        bld = SyncopeClient.getGroupSearchConditionBuilder();
+                        restClient = new GroupRestClient();
+                        break;
+
+                    default:
+                        bld = SyncopeClient.getAnyObjectSearchConditionBuilder(type);
+                        restClient = new AnyObjectRestClient();
+                }
+                fiql = bld.isNotNull("key").query();
+            }
+        }
+
+        @Override
+        protected List<StatusBean> getStatusBeans(final long first, final long count) {
+            List<StatusBean> statusBeans = new ArrayList<>();
+
+            if (fiql != null && restClient != null) {
+                int page = (int) first / paginatorRows;
+                List<? extends AnyTO> result = restClient.search(
+                        SyncopeConstants.ROOT_REALM, fiql, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type);
+
+                CollectionUtils.collect(result, new Transformer<AnyTO, StatusBean>() {
+
+                    @Override
+                    public StatusBean transform(final AnyTO any) {
+                        return StatusUtils.getStatusBean(
+                                any,
+                                resource.getKey(),
+                                null,
+                                any instanceof GroupTO);
+                    }
+                }, statusBeans);
+            }
+
+            return statusBeans;
+        }
+
+        @Override
+        public long size() {
+            return fiql == null
+                    ? 0
+                    : restClient.count(SyncopeConstants.ROOT_REALM, fiql, type);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/status/StatusModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/status/StatusModal.java b/client/console/src/main/java/org/apache/syncope/client/console/status/StatusModal.java
index baba5a5..d1387e0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/status/StatusModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/status/StatusModal.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.status;
 
-import java.io.Serializable;
 import org.apache.syncope.client.console.commons.DirectoryDataProvider;
 import org.apache.syncope.client.console.commons.status.StatusBean;
 import org.apache.syncope.client.console.panels.DirectoryPanel;
@@ -26,10 +25,11 @@ import org.apache.syncope.client.console.panels.ModalPanel;
 import org.apache.syncope.client.console.panels.MultilevelPanel;
 import org.apache.syncope.client.console.rest.AbstractAnyRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.AbstractBaseBean;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.markup.html.panel.Panel;
 
-public abstract class StatusModal<T extends Serializable> extends Panel implements ModalPanel {
+public abstract class StatusModal<T extends AbstractBaseBean> extends Panel implements ModalPanel {
 
     private static final long serialVersionUID = 1066124171682570080L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
index 1a78c2d..48c73f6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
@@ -244,7 +244,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends AjaxWizardBui
             pullTaskSpecifics.add(destinationRealm);
 
             // ------------------------------
-            // Only for pull tasks
+            // Only for push tasks
             // ------------------------------  
             WebMarkupContainer pushTaskSpecifics = new WebMarkupContainer("pushTaskSpecifics");
             add(pushTaskSpecifics.setRenderBodyOnly(true));

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index 5f33ed5..310aa13 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.topology;
 
-
 import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.io.Serializable;
 import java.text.MessageFormat;
@@ -417,7 +416,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
         MetaDataRoleAuthorizationStrategy.authorize(edit, RENDER, StandardEntitlement.RESOURCE_READ);
         fragment.add(edit);
 
-        AjaxLink<String> status = new IndicatingAjaxLink<String>("status") {
+        AjaxLink<String> status = new IndicatingAjaxLink<String>("reconciliation") {
 
             private static final long serialVersionUID = 3776750333491622263L;
 
@@ -426,8 +425,8 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
                 ResourceTO modelObject = resourceRestClient.read(node.getKey());
                 target.add(propTaskModal.setContent(
                         new ResourceStatusModal(propTaskModal, pageRef, modelObject)));
-                propTaskModal.header(new Model<>(MessageFormat.format(getString("resource.provisioning.status"),
-                        node.getKey())));
+                propTaskModal.header(
+                        new Model<>(MessageFormat.format(getString("resource.reconciliation"), node.getKey())));
                 propTaskModal.show(true);
             }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index 5b67fa1..d8e8f77 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@ -89,6 +89,8 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         PROVISION("update"),
         DEPROVISION_MEMBERS("update"),
         PROVISION_MEMBERS("update"),
+        RECONCILIATION_PUSH("update"),
+        RECONCILIATION_PULL("update"),
         MANAGE_RESOURCES("update"),
         MANAGE_USERS("update"),
         MANAGE_GROUPS("update"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconDetailsModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconDetailsModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconDetailsModalPanel.java
new file mode 100644
index 0000000..dfd6047
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconDetailsModalPanel.java
@@ -0,0 +1,178 @@
+/*
+ * 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.widgets;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.rest.BaseRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.widgets.reconciliation.Any;
+import org.apache.syncope.client.console.widgets.reconciliation.Misaligned;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public class ReconDetailsModalPanel extends AbstractModalPanel<Any> {
+
+    private static final long serialVersionUID = 1469396040405535283L;
+
+    private static final int ROWS = 10;
+
+    private final String resource;
+
+    private final List<Misaligned> misaligned;
+
+    public ReconDetailsModalPanel(
+            final BaseModal<Any> modal,
+            final String resource,
+            final List<Misaligned> misaligned,
+            final PageReference pageRef) {
+
+        super(modal, pageRef);
+        this.resource = resource;
+        this.misaligned = misaligned;
+
+        add(new DiffPanel("diff", pageRef));
+    }
+
+    private class DiffPanel extends DirectoryPanel<
+        Misaligned, Misaligned, DetailsProvider, BaseRestClient> {
+
+        private static final long serialVersionUID = -8214546246301342868L;
+
+        DiffPanel(final String id, final PageReference pageRef) {
+            super(id, new Builder<Misaligned, Misaligned, BaseRestClient>(null, pageRef) {
+
+                private static final long serialVersionUID = 8769126634538601689L;
+
+                @Override
+                protected WizardMgtPanel<Misaligned> newInstance(final String id, final boolean wizardInModal) {
+                    throw new UnsupportedOperationException();
+                }
+            }.disableCheckBoxes().hidePaginator());
+
+            rows = 10;
+            initResultTable();
+        }
+
+        @Override
+        protected DetailsProvider dataProvider() {
+            return new DetailsProvider();
+        }
+
+        @Override
+        protected String paginatorRowsKey() {
+            return StringUtils.EMPTY;
+        }
+
+        @Override
+        protected Collection<ActionLink.ActionType> getBulkActions() {
+            return Collections.<ActionLink.ActionType>emptyList();
+        }
+
+        @Override
+        protected List<IColumn<Misaligned, String>> getColumns() {
+            List<IColumn<Misaligned, String>> columns = new ArrayList<>();
+
+            columns.add(new PropertyColumn<Misaligned, String>(new ResourceModel("key"), "name", "name"));
+
+            columns.add(new AbstractColumn<Misaligned, String>(Model.of(Constants.SYNCOPE)) {
+
+                private static final long serialVersionUID = 2054811145491901166L;
+
+                @Override
+                public void populateItem(
+                        final Item<ICellPopulator<Misaligned>> cellItem,
+                        final String componentId,
+                        final IModel<Misaligned> rowModel) {
+
+                    cellItem.add(new Label(componentId, rowModel.getObject().getOnSyncope().toString()));
+                    cellItem.add(new AttributeModifier("class", "code-deletion"));
+                }
+            });
+
+            columns.add(new AbstractColumn<Misaligned, String>(Model.of(resource)) {
+
+                private static final long serialVersionUID = 2054811145491901166L;
+
+                @Override
+                public void populateItem(
+                        final Item<ICellPopulator<Misaligned>> cellItem,
+                        final String componentId,
+                        final IModel<Misaligned> rowModel) {
+
+                    cellItem.add(new Label(componentId, rowModel.getObject().getOnResource().toString()));
+                    cellItem.add(new AttributeModifier("class", "code-addition"));
+                }
+            });
+
+            return columns;
+        }
+    }
+
+    protected final class DetailsProvider extends DirectoryDataProvider<Misaligned> {
+
+        private static final long serialVersionUID = -1500081449932597854L;
+
+        private final SortableDataProviderComparator<Misaligned> comparator;
+
+        private DetailsProvider() {
+            super(ROWS);
+            setSort("name", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<Misaligned> iterator(final long first, final long count) {
+            Collections.sort(misaligned, comparator);
+            return misaligned.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return misaligned.size();
+        }
+
+        @Override
+        public IModel<Misaligned> model(final Misaligned object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationDetailsModalPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationDetailsModalPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationDetailsModalPanel.java
deleted file mode 100644
index 5fd1e62..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationDetailsModalPanel.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.console.widgets;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.commons.DirectoryDataProvider;
-import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
-import org.apache.syncope.client.console.panels.AbstractModalPanel;
-import org.apache.syncope.client.console.panels.DirectoryPanel;
-import org.apache.syncope.client.console.rest.BaseRestClient;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
-import org.apache.syncope.client.console.widgets.reconciliation.Any;
-import org.apache.syncope.client.console.widgets.reconciliation.Misaligned;
-import org.apache.syncope.client.console.wizards.WizardMgtPanel;
-import org.apache.wicket.AttributeModifier;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
-import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
-import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.repeater.Item;
-import org.apache.wicket.model.CompoundPropertyModel;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.ResourceModel;
-
-public class ReconciliationDetailsModalPanel extends AbstractModalPanel<Any> {
-
-    private static final long serialVersionUID = 1469396040405535283L;
-
-    private static final int ROWS = 10;
-
-    private final String resource;
-
-    private final List<Misaligned> misaligned;
-
-    public ReconciliationDetailsModalPanel(
-            final BaseModal<Any> modal,
-            final String resource,
-            final List<Misaligned> misaligned,
-            final PageReference pageRef) {
-
-        super(modal, pageRef);
-        this.resource = resource;
-        this.misaligned = misaligned;
-
-        add(new DiffPanel("diff", pageRef));
-    }
-
-    private class DiffPanel extends DirectoryPanel<
-        Misaligned, Misaligned, DetailsProvider, BaseRestClient> {
-
-        private static final long serialVersionUID = -8214546246301342868L;
-
-        DiffPanel(final String id, final PageReference pageRef) {
-            super(id, new Builder<Misaligned, Misaligned, BaseRestClient>(null, pageRef) {
-
-                private static final long serialVersionUID = 8769126634538601689L;
-
-                @Override
-                protected WizardMgtPanel<Misaligned> newInstance(final String id, final boolean wizardInModal) {
-                    throw new UnsupportedOperationException();
-                }
-            }.disableCheckBoxes().hidePaginator());
-
-            rows = 10;
-            initResultTable();
-        }
-
-        @Override
-        protected DetailsProvider dataProvider() {
-            return new DetailsProvider();
-        }
-
-        @Override
-        protected String paginatorRowsKey() {
-            return StringUtils.EMPTY;
-        }
-
-        @Override
-        protected Collection<ActionLink.ActionType> getBulkActions() {
-            return Collections.<ActionLink.ActionType>emptyList();
-        }
-
-        @Override
-        protected List<IColumn<Misaligned, String>> getColumns() {
-            List<IColumn<Misaligned, String>> columns = new ArrayList<>();
-
-            columns.add(new PropertyColumn<Misaligned, String>(new ResourceModel("key"), "name", "name"));
-
-            columns.add(new AbstractColumn<Misaligned, String>(Model.of("Syncope")) {
-
-                private static final long serialVersionUID = 2054811145491901166L;
-
-                @Override
-                public void populateItem(
-                        final Item<ICellPopulator<Misaligned>> cellItem,
-                        final String componentId,
-                        final IModel<Misaligned> rowModel) {
-
-                    cellItem.add(new Label(componentId, rowModel.getObject().getOnSyncope().toString()));
-                    cellItem.add(new AttributeModifier("class", "code-deletion"));
-                }
-            });
-
-            columns.add(new AbstractColumn<Misaligned, String>(Model.of(resource)) {
-
-                private static final long serialVersionUID = 2054811145491901166L;
-
-                @Override
-                public void populateItem(
-                        final Item<ICellPopulator<Misaligned>> cellItem,
-                        final String componentId,
-                        final IModel<Misaligned> rowModel) {
-
-                    cellItem.add(new Label(componentId, rowModel.getObject().getOnResource().toString()));
-                    cellItem.add(new AttributeModifier("class", "code-addition"));
-                }
-            });
-
-            return columns;
-        }
-    }
-
-    protected final class DetailsProvider extends DirectoryDataProvider<Misaligned> {
-
-        private static final long serialVersionUID = -1500081449932597854L;
-
-        private final SortableDataProviderComparator<Misaligned> comparator;
-
-        private DetailsProvider() {
-            super(ROWS);
-            setSort("name", SortOrder.ASCENDING);
-            comparator = new SortableDataProviderComparator<>(this);
-        }
-
-        @Override
-        public Iterator<Misaligned> iterator(final long first, final long count) {
-            Collections.sort(misaligned, comparator);
-            return misaligned.subList((int) first, (int) first + (int) count).iterator();
-        }
-
-        @Override
-        public long size() {
-            return misaligned.size();
-        }
-
-        @Override
-        public IModel<Misaligned> model(final Misaligned object) {
-            return new CompoundPropertyModel<>(object);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java
index edce805..502fc3f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java
@@ -445,7 +445,7 @@ public class ReconciliationWidget extends BaseWidget {
                                                 rowModel.getObject().getType()
                                                 + " " + rowModel.getObject().getKey()
                                                 + " " + rowModel.getObject().getName()));
-                                        modal.setContent(new ReconciliationDetailsModalPanel(
+                                        modal.setContent(new ReconDetailsModalPanel(
                                                 modal,
                                                 resource,
                                                 misaligned,

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
index 5d925df..025998c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
@@ -49,22 +49,22 @@ public class AnyObjectWizardBuilder extends AnyWizardBuilder<AnyObjectTO> implem
     protected Serializable onApplyInternal(final AnyWrapper<AnyObjectTO> modelObject) {
         final AnyObjectTO inner = modelObject.getInnerObject();
 
-        ProvisioningResult<AnyObjectTO> actual;
+        ProvisioningResult<AnyObjectTO> result;
         if (inner.getKey() == null) {
-            actual = anyObjectRestClient.create(inner);
+            result = anyObjectRestClient.create(inner);
         } else {
             AnyObjectPatch patch = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
 
             // update just if it is changed
             if (patch.isEmpty()) {
-                actual = new ProvisioningResult<>();
-                actual.setEntity(inner);
+                result = new ProvisioningResult<>();
+                result.setEntity(inner);
             } else {
-                actual = anyObjectRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
+                result = anyObjectRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 
-        return actual;
+        return result;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
index 2c75dae..76601f6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
@@ -35,6 +35,7 @@ import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.wicket.Component;
 import org.apache.wicket.behavior.Behavior;
 import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.html.panel.Fragment;
@@ -48,7 +49,12 @@ public class ConnObjectPanel extends Panel {
 
     private static final long serialVersionUID = -6469290753080058487L;
 
-    public ConnObjectPanel(final String id, final Pair<ConnObjectTO, ConnObjectTO> connObjectTOs, final boolean view) {
+    public ConnObjectPanel(
+            final String id,
+            final Pair<IModel<?>, IModel<?>> titles,
+            final Pair<ConnObjectTO, ConnObjectTO> connObjectTOs,
+            final boolean hideLeft) {
+
         super(id);
 
         final IModel<List<String>> formProps = new LoadableDetachableModel<List<String>>() {
@@ -60,12 +66,11 @@ public class ConnObjectPanel extends Panel {
                 List<AttrTO> right = new ArrayList<>(connObjectTOs == null || connObjectTOs.getRight() == null
                         ? Collections.<AttrTO>emptyList()
                         : connObjectTOs.getRight().getAttrs());
-
                 List<AttrTO> left = new ArrayList<>(connObjectTOs == null || connObjectTOs.getLeft() == null
                         ? Collections.<AttrTO>emptyList()
                         : connObjectTOs.getLeft().getAttrs());
 
-                final List<String> schemas = ListUtils.sum(
+                List<String> schemas = ListUtils.sum(
                         CollectionUtils.collect(right, new Transformer<AttrTO, String>() {
 
                             @Override
@@ -80,21 +85,21 @@ public class ConnObjectPanel extends Panel {
                                 return input.getSchema();
                             }
                         }, new ArrayList<String>()));
-
                 Collections.sort(schemas);
-
                 return schemas;
             }
         };
 
-        final Map<String, AttrTO> beforeProfile = connObjectTOs == null || connObjectTOs.getLeft() == null
+        add(new Label("leftTitle", titles.getLeft()).setOutputMarkupPlaceholderTag(true).setVisible(!hideLeft));
+        add(new Label("rightTitle", titles.getRight()));
+
+        final Map<String, AttrTO> leftProfile = connObjectTOs == null || connObjectTOs.getLeft() == null
                 ? null
                 : EntityTOUtils.buildAttrMap(connObjectTOs.getLeft().getAttrs());
-        final Map<String, AttrTO> afterProfile = connObjectTOs == null || connObjectTOs.getRight() == null
+        final Map<String, AttrTO> rightProfile = connObjectTOs == null || connObjectTOs.getRight() == null
                 ? null
                 : EntityTOUtils.buildAttrMap(connObjectTOs.getRight().getAttrs());
-
-        final ListView<String> propView = new ListView<String>("propView", formProps) {
+        ListView<String> propView = new ListView<String>("propView", formProps) {
 
             private static final long serialVersionUID = 3109256773218160485L;
 
@@ -103,29 +108,26 @@ public class ConnObjectPanel extends Panel {
                 final String prop = item.getModelObject();
 
                 final Fragment valueFragment;
-                final AttrTO before = beforeProfile == null ? null : beforeProfile.get(prop);
-                final AttrTO after = afterProfile == null ? null : afterProfile.get(prop);
+                final AttrTO left = leftProfile == null ? null : leftProfile.get(prop);
+                final AttrTO right = rightProfile == null ? null : rightProfile.get(prop);
 
                 valueFragment = new Fragment("value", "doubleValue", ConnObjectPanel.this);
+                valueFragment.add(getValuePanel("leftAttribute", prop, left).
+                        setOutputMarkupPlaceholderTag(true).setVisible(!hideLeft));
+                valueFragment.add(getValuePanel("rightAttribute", prop, right));
+
+                if (left == null || right == null
+                        || (CollectionUtils.isNotEmpty(right.getValues())
+                        && CollectionUtils.isEmpty(left.getValues()))
+                        || (CollectionUtils.isEmpty(right.getValues())
+                        && CollectionUtils.isNotEmpty(left.getValues()))
+                        || (CollectionUtils.isNotEmpty(right.getValues())
+                        && CollectionUtils.isNotEmpty(left.getValues())
+                        && right.getValues().size() != left.getValues().size())
+                        || (CollectionUtils.isNotEmpty(right.getValues())
+                        && CollectionUtils.isNotEmpty(left.getValues())
+                        && !right.getValues().equals(left.getValues()))) {
 
-                Panel oldAttribute = getValuePanel("oldAttribute", prop, before);
-                oldAttribute.setOutputMarkupPlaceholderTag(true);
-                oldAttribute.setVisible(!view);
-                valueFragment.add(oldAttribute);
-
-                valueFragment.add(getValuePanel("newAttribute", prop, after));
-
-                if (before == null || after == null
-                        || (CollectionUtils.isNotEmpty(after.getValues())
-                        && CollectionUtils.isEmpty(before.getValues()))
-                        || (CollectionUtils.isEmpty(after.getValues())
-                        && CollectionUtils.isNotEmpty(before.getValues()))
-                        || (CollectionUtils.isNotEmpty(after.getValues())
-                        && CollectionUtils.isNotEmpty(before.getValues())
-                        && after.getValues().size() != before.getValues().size())
-                        || (CollectionUtils.isNotEmpty(after.getValues())
-                        && CollectionUtils.isNotEmpty(before.getValues())
-                        && !after.getValues().equals(before.getValues()))) {
                     valueFragment.add(new Behavior() {
 
                         private static final long serialVersionUID = 3109256773218160485L;
@@ -169,5 +171,4 @@ public class ConnObjectPanel extends Panel {
         field.setEnabled(false);
         return field;
     }
-
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
index 94b3d37..cb46bf1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
@@ -68,9 +68,9 @@ public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> implements Gro
                 ? GroupWrapper.class.cast(modelObject).fillDynamicConditions()
                 : modelObject.getInnerObject();
 
-        ProvisioningResult<GroupTO> actual;
+        ProvisioningResult<GroupTO> result;
         if (inner.getKey() == null) {
-            actual = groupRestClient.create(inner);
+            result = groupRestClient.create(inner);
         } else {
             GroupPatch patch = AnyOperations.diff(inner, getOriginalItem().getInnerObject(), false);
             GroupTO originaObj = getOriginalItem().getInnerObject();
@@ -88,14 +88,14 @@ public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> implements Gro
 
             // update just if it is changed
             if (patch.isEmpty() && !othersNotEqualsOrBlanks) {
-                actual = new ProvisioningResult<>();
-                actual.setEntity(inner);
+                result = new ProvisioningResult<>();
+                result.setEntity(inner);
             } else {
-                actual = groupRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
+                result = groupRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 
-        return actual;
+        return result;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/StatusPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/StatusPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/StatusPanel.java
index cc1a2cc..1f64372 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/StatusPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/StatusPanel.java
@@ -19,11 +19,14 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.SerializableTransformer;
 import org.apache.syncope.client.console.commons.status.ConnObjectWrapper;
@@ -32,18 +35,22 @@ import org.apache.syncope.client.console.commons.status.StatusBean;
 import org.apache.syncope.client.console.commons.status.StatusUtils;
 import org.apache.syncope.client.console.panels.ListViewPanel;
 import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.panels.PropagationErrorPanel;
 import org.apache.syncope.client.console.panels.RemoteObjectPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.ReconStatus;
 import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.Component;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,8 +62,6 @@ public class StatusPanel extends Panel {
 
     private Map<String, StatusBean> initialStatusBeanMap;
 
-    private final StatusUtils statusUtils;
-
     private ListViewPanel<?> listViewPanel;
 
     public <T extends AnyTO> StatusPanel(
@@ -65,36 +70,38 @@ public class StatusPanel extends Panel {
             final IModel<List<StatusBean>> model,
             final PageReference pageRef) {
         super(id);
-        statusUtils = new StatusUtils();
-        init(any, model,
-                CollectionUtils.collect(statusUtils.getConnectorObjects(any),
-                        new SerializableTransformer<ConnObjectWrapper, Pair<ConnObjectTO, ConnObjectWrapper>>() {
+        init(any, model, CollectionUtils.collect(StatusUtils.getReconStatuses(
+                AnyTypeKind.fromTOClass(any.getClass()), any.getKey(), any.getResources()),
+                new SerializableTransformer<ReconStatus, Triple<ConnObjectTO, ConnObjectWrapper, String>>() {
 
-                    private static final long serialVersionUID = 2658691884036294287L;
+            private static final long serialVersionUID = 2658691884036294287L;
 
-                    @Override
-                    public Pair<ConnObjectTO, ConnObjectWrapper> transform(final ConnObjectWrapper input) {
-                        return Pair.of(null, input);
-                    }
+            @Override
+            public Triple<ConnObjectTO, ConnObjectWrapper, String> transform(final ReconStatus status) {
+                return Triple.of(
+                        status.getOnSyncope(),
+                        new ConnObjectWrapper(any, status.getResource(), status.getOnResource()),
+                        null);
+            }
 
-                }, new ArrayList<Pair<ConnObjectTO, ConnObjectWrapper>>()), pageRef, false);
+        }, new ArrayList<Triple<ConnObjectTO, ConnObjectWrapper, String>>()), pageRef, false);
     }
 
     public <T extends AnyTO> StatusPanel(
             final String id,
             final T any,
             final IModel<List<StatusBean>> model,
-            final List<Pair<ConnObjectTO, ConnObjectWrapper>> connObjects,
+            final List<Triple<ConnObjectTO, ConnObjectWrapper, String>> connObjects,
             final PageReference pageRef) {
+
         super(id);
-        statusUtils = new StatusUtils();
         init(any, model, connObjects, pageRef, true);
     }
 
     private void init(
             final AnyTO any,
             final IModel<List<StatusBean>> model,
-            final List<Pair<ConnObjectTO, ConnObjectWrapper>> connObjects,
+            final List<Triple<ConnObjectTO, ConnObjectWrapper, String>> connObjects,
             final PageReference pageRef,
             final boolean enableConnObjectLink) {
 
@@ -123,15 +130,20 @@ public class StatusPanel extends Panel {
         statusBeans.add(syncope);
         initialStatusBeanMap.put(syncope.getResource(), syncope);
 
-        for (Pair<ConnObjectTO, ConnObjectWrapper> pair : connObjects) {
-            ConnObjectWrapper entry = pair.getRight();
-            final StatusBean statusBean = statusUtils.getStatusBean(entry.getAny(),
-                    entry.getResourceName(),
-                    entry.getConnObjectTO(),
+        final Map<String, String> failureReasons = new HashMap<>();
+        for (Triple<ConnObjectTO, ConnObjectWrapper, String> pair : connObjects) {
+            ConnObjectWrapper connObjectWrapper = pair.getMiddle();
+            StatusBean statusBean = StatusUtils.getStatusBean(connObjectWrapper.getAny(),
+                    connObjectWrapper.getResource(),
+                    connObjectWrapper.getConnObjectTO(),
                     any instanceof GroupTO);
 
-            initialStatusBeanMap.put(entry.getResourceName(), statusBean);
+            initialStatusBeanMap.put(connObjectWrapper.getResource(), statusBean);
             statusBeans.add(statusBean);
+
+            if (StringUtils.isNotBlank(pair.getRight())) {
+                failureReasons.put(connObjectWrapper.getResource(), pair.getRight());
+            }
         }
 
         final MultilevelPanel mlp = new MultilevelPanel("resources");
@@ -150,22 +162,20 @@ public class StatusPanel extends Panel {
                 }
             }
         };
-
         builder.setModel(model);
         builder.setItems(statusBeans);
         builder.includes("resource", "connObjectLink", "status");
         builder.withChecks(ListViewPanel.CheckAvailability.NONE);
         builder.setReuseItem(false);
 
-        final ActionLink<StatusBean> connObjectLink = new ActionLink<StatusBean>() {
+        ActionLink<StatusBean> connObjectLink = new ActionLink<StatusBean>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
             protected boolean statusCondition(final StatusBean bean) {
-                final Pair<ConnObjectTO, ConnObjectTO> pair
-                        = getConnObjectTO(bean.getKey(), bean.getResource(), connObjects);
-
+                Pair<ConnObjectTO, ConnObjectTO> pair =
+                        getConnObjectTOs(bean.getKey(), bean.getResource(), connObjects);
                 return pair != null && pair.getRight() != null;
             }
 
@@ -174,13 +184,26 @@ public class StatusPanel extends Panel {
                 mlp.next(bean.getResource(), new RemoteAnyPanel(bean, connObjects), target);
             }
         };
-
         if (!enableConnObjectLink) {
             connObjectLink.disable();
         }
-
         builder.addAction(connObjectLink, ActionLink.ActionType.VIEW, StandardEntitlement.RESOURCE_GET_CONNOBJECT);
 
+        builder.addAction(new ActionLink<StatusBean>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            protected boolean statusCondition(final StatusBean bean) {
+                return failureReasons.containsKey(bean.getResource());
+            }
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final StatusBean bean) {
+                mlp.next(bean.getResource(), new PropagationErrorPanel(failureReasons.get(bean.getResource())), target);
+            }
+        }, ActionLink.ActionType.PROPAGATION_TASKS, StringUtils.EMPTY);
+
         listViewPanel = ListViewPanel.class.cast(builder.build(MultilevelPanel.FIRST_LEVEL_ID));
         mlp.setFirstLevel(listViewPanel);
     }
@@ -193,39 +216,44 @@ public class StatusPanel extends Panel {
         return initialStatusBeanMap;
     }
 
-    protected Pair<ConnObjectTO, ConnObjectTO> getConnObjectTO(
-            final String anyKey, final String resourceName,
-            final List<Pair<ConnObjectTO, ConnObjectWrapper>> objects) {
+    protected Pair<ConnObjectTO, ConnObjectTO> getConnObjectTOs(
+            final String anyKey,
+            final String resource,
+            final List<Triple<ConnObjectTO, ConnObjectWrapper, String>> objects) {
 
-        for (Pair<ConnObjectTO, ConnObjectWrapper> object : objects) {
-            if (anyKey.equals(object.getRight().getAny().getKey())
-                    && resourceName.equalsIgnoreCase(object.getRight().getResourceName())) {
+        for (Triple<ConnObjectTO, ConnObjectWrapper, String> object : objects) {
+            if (anyKey.equals(object.getMiddle().getAny().getKey())
+                    && resource.equalsIgnoreCase(object.getMiddle().getResource())) {
 
-                return Pair.of(object.getLeft(), object.getRight().getConnObjectTO());
+                return Pair.of(object.getLeft(), object.getMiddle().getConnObjectTO());
             }
         }
 
         return null;
     }
 
-    public class RemoteAnyPanel extends RemoteObjectPanel {
+    class RemoteAnyPanel extends RemoteObjectPanel {
 
         private static final long serialVersionUID = 4303365227411467563L;
 
         private final StatusBean bean;
 
-        private final List<Pair<ConnObjectTO, ConnObjectWrapper>> connObjects;
+        private final List<Triple<ConnObjectTO, ConnObjectWrapper, String>> connObjects;
 
-        public RemoteAnyPanel(final StatusBean bean, final List<Pair<ConnObjectTO, ConnObjectWrapper>> connObjects) {
+        RemoteAnyPanel(final StatusBean bean, final List<Triple<ConnObjectTO, ConnObjectWrapper, String>> connObjects) {
             this.bean = bean;
             this.connObjects = connObjects;
 
-            add(new ConnObjectPanel(REMOTE_OBJECT_PANEL_ID, getConnObjectTO(), false));
+            add(new ConnObjectPanel(
+                    REMOTE_OBJECT_PANEL_ID,
+                    Pair.<IModel<?>, IModel<?>>of(new ResourceModel("before"), new ResourceModel("after")),
+                    getConnObjectTOs(),
+                    false));
         }
 
         @Override
-        protected final Pair<ConnObjectTO, ConnObjectTO> getConnObjectTO() {
-            return StatusPanel.this.getConnObjectTO(bean.getKey(), bean.getResource(), connObjects);
+        protected final Pair<ConnObjectTO, ConnObjectTO> getConnObjectTOs() {
+            return StatusPanel.this.getConnObjectTOs(bean.getKey(), bean.getResource(), connObjects);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/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 4f408e8..cfa78a0 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
@@ -76,9 +76,9 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> implements UserF
     protected Serializable onApplyInternal(final AnyWrapper<UserTO> modelObject) {
         UserTO inner = modelObject.getInnerObject();
 
-        ProvisioningResult<UserTO> actual;
+        ProvisioningResult<UserTO> result;
         if (inner.getKey() == null) {
-            actual = userRestClient.create(inner, modelObject instanceof UserWrapper
+            result = userRestClient.create(inner, modelObject instanceof UserWrapper
                     ? UserWrapper.class.cast(modelObject).isStorePasswordInSyncope()
                     : StringUtils.isNotBlank(inner.getPassword()));
         } else {
@@ -92,14 +92,14 @@ public class UserWizardBuilder extends AnyWizardBuilder<UserTO> implements UserF
 
             // update just if it is changed
             if (patch.isEmpty()) {
-                actual = new ProvisioningResult<>();
-                actual.setEntity(inner);
+                result = new ProvisioningResult<>();
+                result.setEntity(inner);
             } else {
-                actual = userRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
+                result = userRestClient.update(getOriginalItem().getInnerObject().getETagValue(), patch);
             }
         }
 
-        return actual;
+        return result;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
index 76fe2d0..7a162c2 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication.properties
@@ -73,3 +73,5 @@ confirmGlobalLogout=Do you really want to perform global logout?
 administration=Administration
 
 timeout=Operation is taking to long: it will be executed in background. Please check later for the result (errors won't be triggered).
+before=Before
+after=After

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
index c8b1529..dca64f0 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_it.properties
@@ -73,3 +73,5 @@ confirmGlobalLogout=Vuoi davvero procedere al logout globale?
 administration=Amministrazione
 
 timeout=L'operazione sta durando troppo: sar\u00e0 eseguita in background. Verifica il risultato pi\u00f9 tardi (gli errori non saranno notificati).
+before=Prima
+after=Dopo

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
index d69adc5..3c2674b 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ja.properties
@@ -71,3 +71,5 @@ intAttrNameInfo.help=\u30aa\u30fc\u30c8\u30b3\u30f3\u30d7\u30ea\u30fc\u30c8\u306
 confirmGlobalLogout=\u30b0\u30ed\u30fc\u30d0\u30eb\u30ed\u30b0\u30a2\u30a6\u30c8\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b?
 administration=\u7ba1\u7406
 timeout=\u64cd\u4f5c\u306b\u9577\u6642\u9593\u304b\u304b\u3063\u3066\u3044\u307e\u3059\: \u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u3067\u5b9f\u884c\u3055\u308c\u307e\u3059\u3002 \u7d50\u679c\u3092\u5f8c\u3067\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044 (\u30a8\u30e9\u30fc\u306f\u5f15\u304d\u8d77\u3053\u3057\u307e\u305b\u3093)\u3002
+before=Before
+after=After

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
index 492c0c4..118a859 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_pt_BR.properties
@@ -73,3 +73,5 @@ confirmGlobalLogout=Do you really want to perform global logout?
 administration=Administra\u00e7\u00e3o
 
 timeout=Operation is taking to long: it will be executed in background. Please check later for the result (errors won't be triggered).
+before=Before
+after=After

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ru.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ru.properties
index b39225d..6a95add 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/SyncopeConsoleApplication_ru.properties
@@ -72,3 +72,5 @@ confirmGlobalLogout=Do you really want to perform global logout?
 administration=Administration
 
 timeout=Operation is taking to long: it will be executed in background. Please check later for the result (errors won't be triggered).
+before=Before
+after=After

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
index 790398a..5d5d129 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/approvals/ApprovalModal.html
@@ -17,10 +17,7 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <span wicket:id="approval">[APPROVAL]</span>
-    </wicket:panel>
-  </body>
+  <wicket:panel>
+    <span wicket:id="approval">[APPROVAL]</span>
+  </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkActionModal.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkActionModal.html b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkActionModal.html
index a9cd88e..dda032e 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkActionModal.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkActionModal.html
@@ -17,10 +17,7 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:extend>
-      <span wicket:id="content"/>
-    </wicket:extend>
-  </body>
+  <wicket:extend>
+    <span wicket:id="content"/>
+  </wicket:extend>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkContent.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkContent.html b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkContent.html
index 4adf9d7..5cebcb5 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkContent.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/bulk/BulkContent.html
@@ -17,21 +17,18 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:head>
-      <link rel="stylesheet" type="text/css" href="css/bulk.css" media="all"/>
-    </wicket:head>
-    <wicket:panel>
-      <div wicket:id="container" id="selectedObjects" class="table-responsive dataTable">
-        <table class="ui-widget ui-widget-content table-hover pageRowElement" wicket:id="selectedObjects">[DataTable]</table>
+  <wicket:head>
+    <link rel="stylesheet" type="text/css" href="css/bulk.css" media="all"/>
+  </wicket:head>
+  <wicket:panel>
+    <div wicket:id="container" id="selectedObjects" class="table-responsive dataTable">
+      <table class="ui-widget ui-widget-content table-hover pageRowElement" wicket:id="selectedObjects">[DataTable]</table>
 
-        <div class="circular-actions">
-          <div id="inline-actions">
-            <span wicket:id="actions">[Actions]</span>
-          </div>
+      <div class="circular-actions">
+        <div id="inline-actions">
+          <span wicket:id="actions">[Actions]</span>
         </div>
       </div>
-    </wicket:panel>
-  </body>
+    </div>
+  </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
index ac755e8..f74731a 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationTasks.html
@@ -17,7 +17,6 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
   <wicket:panel>
     <span wicket:id="tasks">[TASKS]</span>
   </wicket:panel>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
index c1cbc27..4b5ef37 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Abouts.html
@@ -17,12 +17,9 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <div wicket:id="about" class="form-group">
-        <span wicket:id="abouts">[abouts]</span>
-      </div>
-    </wicket:panel>
-  </body>
+  <wicket:panel>
+    <div wicket:id="about" class="form-group">
+      <span wicket:id="abouts">[abouts]</span>
+    </div>
+  </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
index d9844ab..1cbab58 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Details.html
@@ -17,24 +17,21 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <div class="form-group">
-        <span wicket:id="isActive">[isActive]</span>
-      </div>
-      <div class="form-group">
-        <span wicket:id="sender">[sender]</span>
-      </div>
-      <div class="form-group">
-        <span wicket:id="subject">[subject]</span>
-      </div>
-      <div class="form-group">
-        <span wicket:id="template">[template]</span>
-      </div>
-      <div class="form-group">
-        <span wicket:id="traceLevel">[traceLevel]</span>
-      </div>
-    </wicket:panel>
-  </body>
+  <wicket:panel>
+    <div class="form-group">
+      <span wicket:id="isActive">[isActive]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="sender">[sender]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="subject">[subject]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="template">[template]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="traceLevel">[traceLevel]</span>
+    </div>
+  </wicket:panel>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
index c697099..0001cfd 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Events.html
@@ -17,11 +17,8 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <span wicket:id="eventSelection"/>
-    </wicket:panel>
-  </body>
+  <wicket:panel>
+    <span wicket:id="eventSelection"/>
+  </wicket:panel>
 </html>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
index cb8bc9d..09cccb0 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/notifications/NotificationWizardBuilder$Recipients.html
@@ -17,33 +17,29 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
-  <body>
-    <wicket:panel>
-      <div class="form-group">
-        <span wicket:id="staticRecipients">[staticRecipients]</span>
+  <wicket:panel>
+    <div class="form-group">
+      <span wicket:id="staticRecipients">[staticRecipients]</span>
+    </div>
+    <div class="form-group form-group-inline">
+      <span wicket:id="recipients">[recipients]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="recipientAttrName">[recipientAttrName]</span>
+    </div>
+    <div class="form-group">
+      <span wicket:id="recipientsProviderClassName">[recipientsProviderClassName]</span>
+    </div>
+    <div id="userFilter" class="form-group box">
+      <div id="title">
+        <span for="userNotifications"><wicket:message key="userNotifications"/></span>
       </div>
-      <div class="form-group form-group-inline">
-        <span wicket:id="recipients">[recipients]</span>
+      <div id="warning">
+        <span for="userNotificationsWarning"><wicket:message key="userNotificationsWarning"/></span>
       </div>
-      <div class="form-group">
-        <span wicket:id="recipientAttrName">[recipientAttrName]</span>
+      <div id="check">
+        <span wicket:id="selfAsRecipient">[selfAsRecipient]</span>
       </div>
-      <div class="form-group">
-        <span wicket:id="recipientsProviderClassName">[recipientsProviderClassName]</span>
-      </div>
-      <div id="userFilter" class="form-group box">
-        <div id="title">
-          <span for="userNotifications"><wicket:message key="userNotifications"/></span>
-        </div>
-        <div id="warning">
-          <span for="userNotificationsWarning"><wicket:message key="userNotificationsWarning"/></span>
-        </div>
-        <div id="check">
-          <span wicket:id="selfAsRecipient">[selfAsRecipient]</span>
-        </div>
-      </div>
-    </wicket:panel>
-  </body>
+    </div>
+  </wicket:panel>
 </html>
-

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/panels/MultilevelPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/MultilevelPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/MultilevelPanel.html
index 6c75dfb..f91de30 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/MultilevelPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/MultilevelPanel.html
@@ -17,7 +17,6 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <head><title></title></head>
   <wicket:panel>
     <div wicket:id="firstLevelContainer">
       <span wicket:id="first">[FIRST]</span>

http://git-wip-us.apache.org/repos/asf/syncope/blob/a660a26b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PropagationErrorPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PropagationErrorPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PropagationErrorPanel.html
new file mode 100644
index 0000000..777986d
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PropagationErrorPanel.html
@@ -0,0 +1,24 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:panel>
+    <pre wicket:id="failureReason">
+    </pre>
+  </wicket:panel>
+</html>