You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2016/03/07 16:52:19 UTC

[3/6] syncope git commit: [SYNCOPE-744] Here is the reconciliation status widget

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/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
new file mode 100644
index 0000000..0e5addc
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationDetailsModalPanel.java
@@ -0,0 +1,177 @@
+/*
+ * 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.SearchableDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.panels.AbstractSearchResultPanel;
+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 AbstractSearchResultPanel<
+        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) {
+                    return new DiffPanel(id, pageRef);
+                }
+            }.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 SearchableDataProvider<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/f9f50c57/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
new file mode 100644
index 0000000..1ac8c9a
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ReconciliationWidget.java
@@ -0,0 +1,532 @@
+/*
+ * 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 de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.SearchableDataProvider;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.AbstractSearchResultPanel;
+import org.apache.syncope.client.console.rest.BaseRestClient;
+import org.apache.syncope.client.console.rest.ReportRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.IndicatorAjaxLink;
+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.ActionLinksPanel;
+import org.apache.syncope.client.console.widgets.reconciliation.Any;
+import org.apache.syncope.client.console.widgets.reconciliation.Anys;
+import org.apache.syncope.client.console.widgets.reconciliation.Misaligned;
+import org.apache.syncope.client.console.widgets.reconciliation.Missing;
+import org.apache.syncope.client.console.widgets.reconciliation.ReconciliationReport;
+import org.apache.syncope.client.console.widgets.reconciliation.ReconciliationReportParser;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.JobTO;
+import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.types.ReportExecExportFormat;
+import org.apache.syncope.common.rest.api.service.ReportService;
+import org.apache.wicket.Application;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Component;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+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;
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.protocol.ws.api.WebSocketBehavior;
+import org.apache.wicket.protocol.ws.api.WebSocketPushBroadcaster;
+import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
+import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
+import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage;
+import org.apache.wicket.protocol.ws.api.registry.IKey;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ReconciliationWidget extends AbstractWidget {
+
+    private static final long serialVersionUID = -816175678514035085L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(ReconciliationWidget.class);
+
+    private static final long RECONCILIATION_REPORT_KEY = 2L;
+
+    private static final int ROWS = 10;
+
+    private final BaseModal<Any> detailsModal = new BaseModal<>("detailsModal");
+
+    private final PageReference pageRef;
+
+    private final ReportRestClient restClient = new ReportRestClient();
+
+    private final WebMarkupContainer overlay;
+
+    private boolean checkReconciliationJob = false;
+
+    public ReconciliationWidget(final String id, final PageReference pageRef) {
+        super(id);
+        this.pageRef = pageRef;
+        setOutputMarkupId(true);
+        add(detailsModal);
+
+        overlay = new WebMarkupContainer("overlay");
+        overlay.setOutputMarkupPlaceholderTag(true);
+        overlay.setVisible(false);
+        add(overlay);
+
+        ReportTO reconciliationReport = restClient.read(RECONCILIATION_REPORT_KEY);
+
+        Fragment reportResult = reconciliationReport.getExecutions().isEmpty()
+                ? new Fragment("reportResult", "noExecFragment", this)
+                : buildExecFragment();
+        reportResult.setOutputMarkupId(true);
+        add(reportResult);
+
+        add(new WebSocketBehavior() {
+
+            private static final long serialVersionUID = 3507933905864454312L;
+
+            @Override
+            protected void onConnect(final ConnectedMessage message) {
+                super.onConnect(message);
+
+                SyncopeConsoleSession.get().scheduleAtFixedRate(
+                        new ReconciliationJobInfoUpdater(message), 0, 10, TimeUnit.SECONDS);
+            }
+        });
+
+        add(new IndicatorAjaxLink<Void>("refresh") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                try {
+                    restClient.startExecution(RECONCILIATION_REPORT_KEY, null);
+
+                    overlay.setVisible(true);
+                    target.add(ReconciliationWidget.this);
+
+                    synchronized (this) {
+                        checkReconciliationJob = true;
+                    }
+
+                    info(getString(Constants.OPERATION_SUCCEEDED));
+                } catch (Exception e) {
+                    LOG.error("While starting reconciliation report", e);
+                    error("Could not start reconciliation report");
+                }
+                SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+            }
+        });
+    }
+
+    private Fragment buildExecFragment() {
+        Fragment execFragment = new Fragment("reportResult", "execFragment", this);
+        execFragment.setOutputMarkupId(true);
+
+        Pair<List<ProgressBean>, ReconciliationReport> execResult;
+        try {
+            execResult = parseReconciliationReportExec();
+        } catch (Exception e) {
+            LOG.error("Could not parse the reconciliation report result", e);
+            execResult = Pair.of(Collections.<ProgressBean>emptyList(), null);
+        }
+        final List<ProgressBean> progressBeans = execResult.getLeft();
+        final ReconciliationReport report = execResult.getRight();
+
+        List<ITab> tabs = new ArrayList<>();
+        tabs.add(new AbstractTab(new ResourceModel("summary")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new ProgressesPanel(panelId, report.getRun(), progressBeans);
+            }
+        });
+        tabs.add(new AbstractTab(new ResourceModel("users")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new AnysReconciliationPanel(panelId, report.getUsers(), pageRef);
+            }
+        });
+        tabs.add(new AbstractTab(new ResourceModel("groups")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new AnysReconciliationPanel(panelId, report.getGroups(), pageRef);
+            }
+        });
+        for (final Anys anys : report.getAnyObjects()) {
+            tabs.add(new AbstractTab(Model.of(anys.getAnyType())) {
+
+                private static final long serialVersionUID = -6815067322125799251L;
+
+                @Override
+                public Panel getPanel(final String panelId) {
+                    return new AnysReconciliationPanel(panelId, anys, pageRef);
+                }
+            });
+        }
+
+        execFragment.add(new AjaxBootstrapTabbedPanel<>("execResult", tabs));
+
+        return execFragment;
+    }
+
+    private Pair<List<ProgressBean>, ReconciliationReport> parseReconciliationReportExec() throws IOException {
+        List<ProgressBean> beans = Collections.emptyList();
+        ReconciliationReport report = null;
+
+        ExecTO exec = IterableUtils.find(restClient.listRecentExecutions(ROWS), new Predicate<ExecTO>() {
+
+            @Override
+            public boolean evaluate(final ExecTO exec) {
+                return exec.getRefKey() == RECONCILIATION_REPORT_KEY;
+            }
+        });
+        if (exec == null) {
+            LOG.error("Could not find the last execution of reconciliation report");
+        } else {
+            Object entity = restClient.exportExecutionResult(exec.getKey(), ReportExecExportFormat.XML).getEntity();
+            if (entity instanceof InputStream) {
+                try {
+                    report = ReconciliationReportParser.parse(exec.getEnd(), (InputStream) entity);
+
+                    beans = new ArrayList<>();
+
+                    ProgressBean progressBean = new ProgressBean();
+                    progressBean.setText(getString("users"));
+                    progressBean.setTotal(report.getUsers().getTotal());
+                    progressBean.setFraction(report.getUsers().getTotal() - report.getUsers().getAnys().size());
+                    progressBean.setCssClass("progress-bar-yellow");
+                    beans.add(progressBean);
+
+                    progressBean = new ProgressBean();
+                    progressBean.setText(getString("groups"));
+                    progressBean.setTotal(report.getGroups().getTotal());
+                    progressBean.setFraction(report.getGroups().getTotal() - report.getGroups().getAnys().size());
+                    progressBean.setCssClass("progress-bar-red");
+                    beans.add(progressBean);
+
+                    int i = 0;
+                    for (Anys anys : report.getAnyObjects()) {
+                        progressBean = new ProgressBean();
+                        progressBean.setText(anys.getAnyType());
+                        progressBean.setTotal(anys.getTotal());
+                        progressBean.setFraction(anys.getTotal() - anys.getAnys().size());
+                        progressBean.setCssClass("progress-bar-" + (i % 2 == 0 ? "green" : "aqua"));
+                        beans.add(progressBean);
+
+                        i++;
+                    }
+                } catch (Exception e) {
+                    LOG.error("Could not parse the last execution available of reconciliation report", e);
+                }
+            }
+        }
+
+        return Pair.of(beans, report);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof WebSocketPushPayload) {
+            WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload();
+            if (wsEvent.getMessage() instanceof ReconciliationJobNotRunningMessage) {
+                overlay.setVisible(false);
+
+                addOrReplace(buildExecFragment());
+
+                wsEvent.getHandler().add(ReconciliationWidget.this);
+
+                synchronized (this) {
+                    checkReconciliationJob = false;
+                }
+            }
+        }
+    }
+
+    private class AnysReconciliationPanel extends AbstractSearchResultPanel<
+        Any, Any, AnysReconciliationProvider, BaseRestClient> {
+
+        private static final long serialVersionUID = -8214546246301342868L;
+
+        private final Anys anys;
+
+        AnysReconciliationPanel(final String id, final Anys anys, final PageReference pageRef) {
+            super(id, new Builder<Any, Any, BaseRestClient>(null, pageRef) {
+
+                private static final long serialVersionUID = 8769126634538601689L;
+
+                @Override
+                protected WizardMgtPanel<Any> newInstance(final String id) {
+                    return new AnysReconciliationPanel(id, anys, pageRef);
+                }
+            }.disableCheckBoxes().hidePaginator());
+
+            this.anys = anys;
+            this.rows = ROWS;
+            initResultTable();
+        }
+
+        @Override
+        protected AnysReconciliationProvider dataProvider() {
+            return new AnysReconciliationProvider(anys);
+        }
+
+        @Override
+        protected String paginatorRowsKey() {
+            return StringUtils.EMPTY;
+        }
+
+        @Override
+        protected Collection<ActionLink.ActionType> getBulkActions() {
+            return Collections.<ActionLink.ActionType>emptyList();
+        }
+
+        @Override
+        protected List<IColumn<Any, String>> getColumns() {
+            List<IColumn<Any, String>> columns = new ArrayList<>();
+
+            columns.add(new AbstractColumn<Any, String>(new ResourceModel("reference"), "key") {
+
+                private static final long serialVersionUID = -1822504503325964706L;
+
+                @Override
+                public void populateItem(
+                        final Item<ICellPopulator<Any>> cellItem,
+                        final String componentId,
+                        final IModel<Any> rowModel) {
+
+                    cellItem.add(new Label(componentId,
+                            rowModel.getObject().getKey()
+                            + (StringUtils.isBlank(rowModel.getObject().getName())
+                            ? StringUtils.EMPTY
+                            : " " + rowModel.getObject().getName())));
+                }
+            });
+
+            final Set<String> resources = new HashSet<>();
+            for (Any any : anys.getAnys()) {
+                resources.addAll(CollectionUtils.collect(any.getMissing(), new Transformer<Missing, String>() {
+
+                    @Override
+                    public String transform(final Missing input) {
+                        return input.getResource();
+                    }
+                }));
+                resources.addAll(CollectionUtils.collect(any.getMisaligned(), new Transformer<Misaligned, String>() {
+
+                    @Override
+                    public String transform(final Misaligned input) {
+                        return input.getResource();
+                    }
+                }));
+            }
+            for (final String resource : resources) {
+                columns.add(new AbstractColumn<Any, String>(Model.of(resource)) {
+
+                    private static final long serialVersionUID = -1822504503325964706L;
+
+                    @Override
+                    public void populateItem(
+                            final Item<ICellPopulator<Any>> cellItem,
+                            final String componentId,
+                            final IModel<Any> rowModel) {
+
+                        final Any any = rowModel.getObject();
+
+                        Missing missing = IterableUtils.find(any.getMissing(), new Predicate<Missing>() {
+
+                            @Override
+                            public boolean evaluate(final Missing object) {
+                                return resource.equals(object.getResource());
+                            }
+                        });
+                        final List<Misaligned> misaligned = CollectionUtils.select(
+                                any.getMisaligned(), new Predicate<Misaligned>() {
+
+                            @Override
+                            public boolean evaluate(final Misaligned object) {
+                                return resource.equals(object.getResource());
+                            }
+                        }, new ArrayList<Misaligned>());
+                        Component content = missing == null
+                                ? misaligned == null || misaligned.isEmpty()
+                                        ? new Label(componentId, StringUtils.EMPTY)
+                                        : ActionLinksPanel.<Any>builder().add(new ActionLink<Any>() {
+
+                                            private static final long serialVersionUID = -3722207913631435501L;
+
+                                            @Override
+                                            public void onClick(final AjaxRequestTarget target, final Any ignore) {
+                                                modal.header(Model.of(
+                                                        rowModel.getObject().getType()
+                                                        + " " + rowModel.getObject().getKey()
+                                                        + " " + rowModel.getObject().getName()));
+                                                modal.setContent(new ReconciliationDetailsModalPanel(
+                                                        modal,
+                                                        resource,
+                                                        misaligned,
+                                                        ReconciliationWidget.this.pageRef));
+                                                modal.show(true);
+                                                target.add(modal);
+                                            }
+                                        }, ActionLink.ActionType.VIEW).
+                                        build(componentId)
+                                : ActionLinksPanel.<Any>builder().add(null, ActionLink.ActionType.NOT_FOND).
+                                build(componentId);
+                        cellItem.add(content);
+                        cellItem.add(new AttributeModifier("class", "text-center"));
+                    }
+                });
+            }
+
+            return columns;
+        }
+    }
+
+    protected final class AnysReconciliationProvider extends SearchableDataProvider<Any> {
+
+        private static final long serialVersionUID = -1500081449932597854L;
+
+        private final Anys anys;
+
+        private final SortableDataProviderComparator<Any> comparator;
+
+        private AnysReconciliationProvider(final Anys anys) {
+            super(ROWS);
+            this.anys = anys;
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<Any> iterator(final long first, final long count) {
+            Collections.sort(anys.getAnys(), comparator);
+            return anys.getAnys().subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return anys.getAnys().size();
+        }
+
+        @Override
+        public IModel<Any> model(final Any object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+
+    protected final class ReconciliationJobInfoUpdater implements Runnable {
+
+        private final String applicationName;
+
+        private final SyncopeConsoleSession session;
+
+        private final IKey key;
+
+        public ReconciliationJobInfoUpdater(final ConnectedMessage message) {
+            this.applicationName = message.getApplication().getName();
+            this.session = SyncopeConsoleSession.get();
+            this.key = message.getKey();
+        }
+
+        @Override
+        public void run() {
+            synchronized (ReconciliationWidget.this) {
+                if (ReconciliationWidget.this.checkReconciliationJob) {
+                    try {
+                        Application application = Application.get(applicationName);
+                        ThreadContext.setApplication(application);
+                        ThreadContext.setSession(session);
+
+                        JobTO reportJobTO = IterableUtils.find(session.getService(ReportService.class).listJobs(),
+                                new Predicate<JobTO>() {
+
+                            @Override
+                            public boolean evaluate(final JobTO jobTO) {
+                                return RECONCILIATION_REPORT_KEY == jobTO.getRefKey();
+                            }
+                        });
+                        if (reportJobTO != null && !reportJobTO.isRunning()) {
+                            LOG.debug("Report {} is not running", RECONCILIATION_REPORT_KEY);
+
+                            WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application);
+                            WebSocketPushBroadcaster broadcaster =
+                                    new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry());
+                            broadcaster.broadcast(
+                                    new ConnectedMessage(application, session.getId(), key),
+                                    new ReconciliationJobNotRunningMessage());
+                        }
+                    } catch (Throwable t) {
+                        LOG.error("Unexpected error while checking for updated reconciliation job info", t);
+                    } finally {
+                        ThreadContext.detach();
+                    }
+                }
+            }
+        }
+    }
+
+    private static class ReconciliationJobNotRunningMessage implements IWebSocketPushMessage, Serializable {
+
+        private static final long serialVersionUID = -824793424112532838L;
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Any.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Any.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Any.java
new file mode 100644
index 0000000..5e915b4
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Any.java
@@ -0,0 +1,71 @@
+/*
+ * 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.reconciliation;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+public class Any extends AbstractBaseBean {
+
+    private static final long serialVersionUID = 2421645848049271898L;
+
+    private String type;
+
+    private long key;
+
+    private String name;
+
+    private final List<Missing> missing = new ArrayList<>();
+
+    private final List<Misaligned> misaligned = new ArrayList<>();
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(final String type) {
+        this.type = type;
+    }
+
+    public long getKey() {
+        return key;
+    }
+
+    public void setKey(final long key) {
+        this.key = key;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public List<Missing> getMissing() {
+        return missing;
+    }
+
+    public List<Misaligned> getMisaligned() {
+        return misaligned;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Anys.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Anys.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Anys.java
new file mode 100644
index 0000000..fecdfee
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Anys.java
@@ -0,0 +1,55 @@
+/*
+ * 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.reconciliation;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+public class Anys extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -2482591351364634179L;
+
+    private int total = 0;
+
+    private String anyType;
+
+    private final List<Any> anys = new ArrayList<>();
+
+    public int getTotal() {
+        return total;
+    }
+
+    public void setTotal(final int total) {
+        this.total = total;
+    }
+
+    public String getAnyType() {
+        return anyType;
+    }
+
+    public void setAnyType(final String anyType) {
+        this.anyType = anyType;
+    }
+
+    public List<Any> getAnys() {
+        return anys;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Misaligned.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Misaligned.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Misaligned.java
new file mode 100644
index 0000000..bd189e6
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Misaligned.java
@@ -0,0 +1,51 @@
+/*
+ * 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.reconciliation;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Misaligned extends Missing {
+
+    private static final long serialVersionUID = -2287634884015557714L;
+
+    private final String name;
+
+    private final Set<String> onSyncope = new HashSet<>();
+
+    private final Set<String> onResource = new HashSet<>();
+
+    public Misaligned(final String resource, final String connObjectKeyValue, final String name) {
+        super(resource, connObjectKeyValue);
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Set<String> getOnSyncope() {
+        return onSyncope;
+    }
+
+    public Set<String> getOnResource() {
+        return onResource;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Missing.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Missing.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Missing.java
new file mode 100644
index 0000000..2d0254a
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/Missing.java
@@ -0,0 +1,44 @@
+/*
+ * 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.reconciliation;
+
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+public class Missing extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -4779715117027316991L;
+
+    private final String resource;
+
+    private final String connObjectKeyValue;
+
+    public Missing(final String resource, final String connObjectKeyValue) {
+        this.resource = resource;
+        this.connObjectKeyValue = connObjectKeyValue;
+    }
+
+    public String getResource() {
+        return resource;
+    }
+
+    public String getConnObjectKeyValue() {
+        return connObjectKeyValue;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReport.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReport.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReport.java
new file mode 100644
index 0000000..b107426
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReport.java
@@ -0,0 +1,66 @@
+/*
+ * 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.reconciliation;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+public class ReconciliationReport extends AbstractBaseBean {
+
+    private static final long serialVersionUID = 931063230006747313L;
+
+    private final Date run;
+
+    private Anys users;
+
+    private Anys groups;
+
+    private final List<Anys> anyObjects = new ArrayList<>();
+
+    public ReconciliationReport(final Date run) {
+        this.run = run;
+    }
+
+    public Date getRun() {
+        return run == null ? null : new Date(run.getTime());
+    }
+
+    public Anys getUsers() {
+        return users;
+    }
+
+    public void setUsers(final Anys users) {
+        this.users = users;
+    }
+
+    public Anys getGroups() {
+        return groups;
+    }
+
+    public void setGroups(final Anys groups) {
+        this.groups = groups;
+    }
+
+    public List<Anys> getAnyObjects() {
+        return anyObjects;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReportParser.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReportParser.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReportParser.java
new file mode 100644
index 0000000..c99b7e3
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/reconciliation/ReconciliationReportParser.java
@@ -0,0 +1,192 @@
+/*
+ * 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.reconciliation;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+
+public final class ReconciliationReportParser {
+
+    private static final XMLInputFactory INPUT_FACTORY = XMLInputFactory.newInstance();
+
+    public static ReconciliationReport parse(final Date run, final InputStream in) throws XMLStreamException {
+        XMLStreamReader streamReader = INPUT_FACTORY.createXMLStreamReader(in);
+        streamReader.nextTag(); // root
+        streamReader.nextTag(); // report
+        streamReader.nextTag(); // reportlet
+
+        ReconciliationReport report = new ReconciliationReport(run);
+
+        List<Missing> missing = new ArrayList<>();
+        List<Misaligned> misaligned = new ArrayList<>();
+        Set<String> onSyncope = null;
+        Set<String> onResource = null;
+
+        Any user = null;
+        Any group = null;
+        Any anyObject = null;
+        String lastAnyType = null;
+        while (streamReader.hasNext()) {
+            if (streamReader.isStartElement()) {
+                switch (streamReader.getLocalName()) {
+                    case "users":
+                        Anys users = new Anys();
+                        users.setTotal(Integer.valueOf(streamReader.getAttributeValue("", "total")));
+                        report.setUsers(users);
+                        break;
+
+                    case "user":
+                        user = new Any();
+                        user.setType(AnyTypeKind.USER.name());
+                        user.setKey(Long.valueOf(streamReader.getAttributeValue("", "key")));
+                        user.setName(streamReader.getAttributeValue("", "username"));
+                        report.getUsers().getAnys().add(user);
+                        break;
+
+                    case "groups":
+                        Anys groups = new Anys();
+                        groups.setTotal(Integer.valueOf(streamReader.getAttributeValue("", "total")));
+                        report.setGroups(groups);
+                        break;
+
+                    case "group":
+                        group = new Any();
+                        group.setType(AnyTypeKind.GROUP.name());
+                        group.setKey(Long.valueOf(streamReader.getAttributeValue("", "key")));
+                        group.setName(streamReader.getAttributeValue("", "groupName"));
+                        report.getGroups().getAnys().add(group);
+                        break;
+
+                    case "anyObjects":
+                        lastAnyType = streamReader.getAttributeValue("", "type");
+                        Anys anyObjects = new Anys();
+                        anyObjects.setAnyType(lastAnyType);
+                        anyObjects.setTotal(Integer.valueOf(streamReader.getAttributeValue("", "total")));
+                        report.getAnyObjects().add(anyObjects);
+                        break;
+
+                    case "anyObject":
+                        anyObject = new Any();
+                        anyObject.setType(lastAnyType);
+                        anyObject.setKey(Long.valueOf(streamReader.getAttributeValue("", "key")));
+                        final String anyType = lastAnyType;
+                        IterableUtils.find(report.getAnyObjects(), new Predicate<Anys>() {
+
+                            @Override
+                            public boolean evaluate(final Anys anys) {
+                                return anyType.equals(anys.getAnyType());
+                            }
+                        }).getAnys().add(anyObject);
+                        break;
+
+                    case "missing":
+                        missing.add(new Missing(
+                                streamReader.getAttributeValue("", "resource"),
+                                streamReader.getAttributeValue("", "connObjectKeyValue")));
+                        break;
+
+                    case "misaligned":
+                        misaligned.add(new Misaligned(
+                                streamReader.getAttributeValue("", "resource"),
+                                streamReader.getAttributeValue("", "connObjectKeyValue"),
+                                streamReader.getAttributeValue("", "name")));
+                        break;
+
+                    case "onSyncope":
+                        onSyncope = new HashSet<>();
+                        break;
+
+                    case "onResource":
+                        onResource = new HashSet<>();
+                        break;
+
+                    case "value":
+                        Set<String> set = onSyncope == null ? onResource : onSyncope;
+                        set.add(streamReader.getElementText());
+                        break;
+
+                    default:
+                }
+            } else if (streamReader.isEndElement()) {
+                switch (streamReader.getLocalName()) {
+                    case "user":
+                        user.getMissing().addAll(missing);
+                        user.getMisaligned().addAll(misaligned);
+                        missing.clear();
+                        misaligned.clear();
+                        break;
+
+                    case "group":
+                        group.getMissing().addAll(missing);
+                        group.getMisaligned().addAll(misaligned);
+                        missing.clear();
+                        misaligned.clear();
+                        break;
+
+                    case "anyObject":
+                        anyObject.getMissing().addAll(missing);
+                        anyObject.getMisaligned().addAll(misaligned);
+                        missing.clear();
+                        misaligned.clear();
+                        break;
+
+                    case "onSyncope":
+                        misaligned.get(misaligned.size() - 1).getOnSyncope().addAll(onSyncope);
+                        onSyncope = null;
+                        break;
+
+                    case "onResource":
+                        misaligned.get(misaligned.size() - 1).getOnResource().addAll(onResource);
+                        onResource = null;
+                        break;
+
+                    default:
+                }
+
+            }
+
+            streamReader.next();
+        }
+
+        try {
+            System.out.println("QQQQQQQQQ\n" + new ObjectMapper().writeValueAsString(report));
+        } catch (JsonProcessingException ex) {
+            System.err.println("nuuuuuuuuuuuuuuuuuuuuuuuu");
+            ex.printStackTrace();
+        }
+
+        return report;
+    }
+
+    private ReconciliationReportParser() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
index dc883bf..59bb335 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
@@ -19,8 +19,8 @@
 package org.apache.syncope.client.console.wizards;
 
 import java.io.Serializable;
+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.panels.ModalPanel;
 import org.apache.wicket.Component;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -87,7 +87,7 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard implemen
             send(AjaxWizard.this, Broadcast.BUBBLE, new NewItemCancelEvent<>(item, target));
         } catch (Exception e) {
             LOG.warn("Wizard error on cancel", e);
-            error(getString(Constants.ERROR) + ": " + e.getMessage());
+            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
             SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
         }
     }
@@ -103,7 +103,7 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard implemen
             send(AjaxWizard.this, Broadcast.BUBBLE, new NewItemFinishEvent<>(item, target).setResult(res));
         } catch (Exception e) {
             LOG.error("Wizard error on finish", e);
-            error(getString(Constants.ERROR) + ": " + e.getMessage());
+            error(StringUtils.isBlank(e.getMessage()) ? e.getClass().getName() : e.getMessage());
             SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
index 6d295f7..34dc4a3 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -82,9 +82,9 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
     /**
      * Modal window.
      */
-    protected final BaseModal<T> altDefaultModal = new BaseModal<T>("alternativeDefaultModal");
+    protected final BaseModal<T> altDefaultModal = new BaseModal<>("alternativeDefaultModal");
     
-    protected final BaseModal<T> displayAttributeModal = new BaseModal<T>("displayAttributeModal");
+    protected final BaseModal<T> displayAttributeModal = new BaseModal<>("displayAttributeModal");
 
     protected WizardMgtPanel(final String id) {
         this(id, false);
@@ -166,10 +166,9 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
                 if (wizardInModal && showResultPage) {
                     modal.setContent(new ResultPage<T>(
                             item,
-                            AjaxWizard.NewItemFinishEvent.class.cast(newItemEvent).getResult(),
-                            pageRef) {
+                            AjaxWizard.NewItemFinishEvent.class.cast(newItemEvent).getResult()) {
 
-                        private static final long serialVersionUID = 1L;
+                        private static final long serialVersionUID = -2630573849050255233L;
 
                         @Override
                         protected void closeAction(final AjaxRequestTarget target) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/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 fafdfb0..fc2a941 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
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.console.wizards.any;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -153,7 +154,7 @@ public class Relationships extends WizardStep {
             }
         });
 
-        viewFragment.add(ActionLinksPanel.<RelationshipTO>builder(pageRef).add(new ActionLink<RelationshipTO>() {
+        viewFragment.add(ActionLinksPanel.<RelationshipTO>builder().add(new ActionLink<RelationshipTO>() {
 
             private static final long serialVersionUID = 3257738274365467945L;
 
@@ -192,9 +193,7 @@ public class Relationships extends WizardStep {
     }
 
     private void addNewRelationships(final RelationshipTO... rels) {
-        for (RelationshipTO relationship : rels) {
-            getCurrentRelationships().add(relationship);
-        }
+        getCurrentRelationships().addAll(Arrays.asList(rels));
     }
 
     private void removeRelationships(
@@ -330,7 +329,7 @@ public class Relationships extends WizardStep {
 
                         anyObjectSearchPanel = new AnyObjectSearchPanel.Builder(
                                 anyType.getKey(),
-                                new ListModel<SearchClause>(new ArrayList<SearchClause>())).
+                                new ListModel<>(new ArrayList<SearchClause>())).
                                 enableSearch().
                                 build("searchPanel");
                         fragment.add(anyObjectSearchPanel.setRenderBodyOnly(true));

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ResultPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ResultPage.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ResultPage.java
index 060a027..45b4aa3 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ResultPage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/ResultPage.java
@@ -23,7 +23,6 @@ import org.apache.syncope.client.console.panels.ModalPanel;
 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.ActionLinksPanel;
-import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -38,14 +37,14 @@ public abstract class ResultPage<T extends Serializable> extends Panel implement
 
     private final T item;
 
-    public ResultPage(final T item, final Serializable result, final PageReference pageRef) {
+    public ResultPage(final T item, final Serializable result) {
         super(BaseModal.CONTENT_ID);
         setOutputMarkupId(true);
         this.item = item;
 
         add(customResultBody("customResultBody", item, result));
 
-        add(ActionLinksPanel.<T>builder(pageRef).add(new ActionLink<T>() {
+        add(ActionLinksPanel.<T>builder().add(new ActionLink<T>() {
 
             private static final long serialVersionUID = 3257738274365467945L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/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 bfb194b..14d1d24 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
@@ -177,7 +177,7 @@ public class StatusPanel extends Panel {
             public void onClick(final AjaxRequestTarget target, final StatusBean bean) {
                 mlp.next(bean.getResourceName(), new RemoteObjectPanel(bean, connObjects), target);
             }
-        }, ActionLink.ActionType.SEARCH, StandardEntitlement.RESOURCE_GET_CONNOBJECT);
+        }, ActionLink.ActionType.VIEW, StandardEntitlement.RESOURCE_GET_CONNOBJECT);
 
         listViewPanel = ListViewPanel.class.cast(builder.build(MultilevelPanel.FIRST_LEVEL_ID));
         mlp.setFirstLevel(listViewPanel);

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
index 0f71eeb..c3940eb 100644
--- a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
+++ b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
@@ -531,3 +531,13 @@ END - Result page
 .nav-tabs-custom > .nav-tabs > li.active {
   border-top-color: #d2d6de !important;
 }
+
+.code-deletion {
+  background-color: #ffdddd;
+  border-color: #f1c0c0;
+}
+
+.code-addition {
+  background-color: #dbffdb;
+  border-color: #c1e9c1;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.html
index 200d752..e2c5948 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.html
@@ -17,23 +17,34 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+  <wicket:head>
+    <script type="text/javascript">
+      $(document).ready(function () {
+        $("#pageTitleInBradcrumb").append($("#pageTitle").text());
+      });
+    </script>
+  </wicket:head>
   <wicket:extend>
+    <script lang="text/javascript">
+
+    </script>
     <section class="content-header">
       <h1>&nbsp;</h1>
       <ol class="breadcrumb">
         <li><a wicket:id="dashboardBr"><i class="fa fa-dashboard"></i> <wicket:message key="dashboard"/></a></li>
-        <li class="active"><wicket:message key="realms"/></li>
+        <li class="active" id="pageTitleInBradcrumb"/>
       </ol>
+      <div id="pageTitle" style="visibility: hidden;"><wicket:message key='realms'/></div>
     </section>
 
     <section class="content" wicket:id="content">
       <div class="box">
         <div class="box-body">
-          <span wicket:id="realmChoicePanel">[Realm Sidebar Panel]</span>
-          <wicket:container wicket:id="body"></wicket:container>
+          <span wicket:id="realmChoicePanel"/>
+          <wicket:container wicket:id="body"/>
         </div>
       </div>
-      <div wicket:id="modal"></div>
+      <div wicket:id="modal"/>
     </section>
   </wicket:extend>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/panels/DashboardControlPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/DashboardControlPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/DashboardControlPanel.html
index ae731af..bc6f762 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/DashboardControlPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/DashboardControlPanel.html
@@ -23,5 +23,11 @@ under the License.
         <span wicket:id="job"/>
       </div>
     </div>
+
+    <div class="row">
+      <div class="col-md-12">
+        <span wicket:id="reconciliation"/>
+      </div>
+    </div>
   </wicket:panel>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/panels/ParametersPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ParametersPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ParametersPanel.html
index 4b6bc36..3550ace 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ParametersPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ParametersPanel.html
@@ -18,6 +18,6 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:extend>
-    <div wicket:id="modalDetails" />
+    <div wicket:id="modalDetails"/>
   </wicket:extend>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/panels/WorkflowTogglePanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/WorkflowTogglePanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/WorkflowTogglePanel.html
index 9cbe6ba..cb02567 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/WorkflowTogglePanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/WorkflowTogglePanel.html
@@ -17,33 +17,30 @@ 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>toggle menu</title></head>
-  <body>
-    <wicket:head>
-      <script type="text/javascript">
-        $(document).ready(function () {
-          $("div.inactive-topology-menu").toggle("slow");
-          $("div.inactive-topology-menu").attr("class", "topology-menu active-topology-menu");
-        });
-      </script>
+  <wicket:head>
+    <script type="text/javascript">
+      $(document).ready(function () {
+        $("div.inactive-topology-menu").toggle("slow");
+        $("div.inactive-topology-menu").attr("class", "topology-menu active-topology-menu");
+      });
+    </script>
 
-      <style type="text/css">
-        div.topology-menu {
-          top: 65px !important;
-        }
+    <style type="text/css">
+      div.topology-menu {
+        top: 65px !important;
+      }
 
-        div.topology-menu div.header {
-          display: none !important;
-        }
-      </style>
-    </wicket:head>
-    <wicket:extend>
-      <div wicket:id="container">
-        <ul class="menu">
-          <li><i class="fa fa-file-image-o"></i><a href="#" wicket:id="activitiModeler">Activiti Modeler</a></li>
-          <li><i class="fa fa-file-text-o"></i><a href="#" wicket:id="xmlEditor">XML editor</a></li>
-        </ul>
-      </div>
-    </wicket:extend>
-  </body>
+      div.topology-menu div.header {
+        display: none !important;
+      }
+    </style>
+  </wicket:head>
+  <wicket:extend>
+    <div wicket:id="container">
+      <ul class="menu">
+        <li><i class="fa fa-file-image-o"></i><a href="#" wicket:id="activitiModeler">Activiti Modeler</a></li>
+        <li><i class="fa fa-file-text-o"></i><a href="#" wicket:id="xmlEditor">XML editor</a></li>
+      </ul>
+    </div>
+  </wicket:extend>
 </html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
index 8829644..78d0927 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.html
@@ -34,6 +34,8 @@ under the License.
     <span wicket:id="panelExecute">[plus]</span>
     <span wicket:id="panelEnable">[plus]</span>
     <span wicket:id="panelSearch">[plus]</span>
+    <span wicket:id="panelNotFound">[plus]</span>
+    <span wicket:id="panelView">[plus]</span>
     <span wicket:id="panelMapping">[plus]</span>
     <span wicket:id="panelAccountLink">[plus]</span>
     <span wicket:id="panelResetTime">[plus]</span>
@@ -96,6 +98,14 @@ under the License.
       <a href="#" wicket:id="createLink" class="btn"><i class="glyphicon glyphicon-plus" alt="create icon" title="Create"></i></a>
     </wicket:fragment>
 
+    <wicket:fragment wicket:id="fragmentNotFound">
+      <i class="fa fa-eye-slash" alt="notFound icon" title="Not Found"></i>
+    </wicket:fragment>
+
+    <wicket:fragment wicket:id="fragmentView">
+      <a href="#" wicket:id="viewLink" class="btn"><i class="fa fa-eye" alt="view icon" title="View"></i></a>
+    </wicket:fragment>
+
     <wicket:fragment wicket:id="fragmentSearch">
       <a href="#" wicket:id="searchLink" class="btn"><i class="glyphicon glyphicon-search" alt="search icon" title="Search"></i></a>
     </wicket:fragment>

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/AnyByRealmWidget.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/AnyByRealmWidget.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/AnyByRealmWidget.html
index c2f9ce3..a476bc2 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/AnyByRealmWidget.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/AnyByRealmWidget.html
@@ -18,7 +18,7 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="box">
+    <div class="box box-default box-solid">
       <div class="box-header with-border">
         <h3 class="box-title"><wicket:message key="usersGroupsAndAnyObjectsDistribution"/></h3>
         <div class="box-tools pull-right">

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/CompletenessWidget.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/CompletenessWidget.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/CompletenessWidget.html
index acc12b3..2e48d08 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/CompletenessWidget.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/CompletenessWidget.html
@@ -18,7 +18,7 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="box">
+    <div class="box box-default box-solid">
       <div class="box-header with-border">
         <h3 class="box-title"><wicket:message key="configurationStatus"/></h3>
         <div class="box-tools pull-right">

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.html
index bc19be2..fdf5373 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.html
@@ -18,20 +18,16 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="nav-tabs-custom">
-      <ul class="nav nav-tabs pull-right ui-sortable-handle">
-        <li class="active"><a data-toggle="tab" href="#running">Available</a></li>
-        <li><a data-toggle="tab" href="#recent">Recent</a></li>
-        <li class="pull-left header"><i class="fa fa-gears"></i> Jobs</li>
-      </ul>
-      <div class="tab-content no-padding">
-        <div style="position: relative;" id="running" class="chart tab-pane active">
-          <span wicket:id="available"/>
-        </div>
-        <div style="position: relative;" id="recent" class="chart tab-pane">
-          <span wicket:id="recent"/>
+    <div class="box box-default box-solid">
+      <div class="box-header with-border">
+        <h3 class="box-title"><wicket:message key="jobs"/></h3>
+        <div class="box-tools pull-right">
+          <button class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
         </div>
       </div>
+      <div class="box-body">
+        <div class="box-body" wicket:id="tabbedPanel"/>
+      </div>
     </div>
   </wicket:panel>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.properties
index 35dbb2e..273253a 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget.properties
@@ -20,3 +20,6 @@ scheduled=Scheduled
 start=Start
 message=Message
 end=End
+available=Available
+recent=Recent
+jobs=Jobs

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_it.properties
index 3738c3c..19a26b9 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_it.properties
@@ -20,3 +20,6 @@ scheduled=Programmato
 start=Inizio
 message=Messaggio
 end=Fine
+available=Disponibili
+recent=Recenti
+jobs=Job

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_pt_BR.properties
index 02411ab..e300f11 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/JobWidget_pt_BR.properties
@@ -20,3 +20,6 @@ scheduled=Programado
 start=Come\u00e7o
 message=Mensagem
 end=Final
+available=Dispon\u00edvel
+recent=Recente
+jobs=Job

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/LoadWidget.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/LoadWidget.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/LoadWidget.html
index d6a4260..8ce2e9f 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/LoadWidget.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/LoadWidget.html
@@ -18,7 +18,7 @@ under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
   <wicket:panel>
-    <div class="box">
+    <div class="box box-default box-solid">
       <div class="box-header with-border">
         <h3 class="box-title"><wicket:message key="systemLoad"/></h3>
         <div class="box-tools pull-right">

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressPanel.html
new file mode 100644
index 0000000..98550e5
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressPanel.html
@@ -0,0 +1,30 @@
+<!--
+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>
+    <div class="progress-group">
+      <span class="progress-text" wicket:id="text"></span>
+      <span class="progress-number"><b><span wicket:id="fraction"/></b>/<span wicket:id="total"/></span>
+
+      <div class="progress sm">
+        <div wicket:id="progress" class="progress-bar progress-bar-striped"></div>
+      </div>
+    </div>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.html
new file mode 100644
index 0000000..7aba758
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.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>
+    <div class="text-right"><em><wicket:message key="lastUpdateMsg"/>: <span wicket:id="lastUpdate"/></em></div><br/>
+    <span wicket:id="progresses"><span wicket:id="progress"/></span>
+  </wicket:panel>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.properties
new file mode 100644
index 0000000..41f94c6
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel.properties
@@ -0,0 +1,17 @@
+# 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.
+lastUpdateMsg=Last Update

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_it.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_it.properties
new file mode 100644
index 0000000..c32f22c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_it.properties
@@ -0,0 +1,17 @@
+# 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.
+lastUpdateMsg=Ultimo Aggiornamento

http://git-wip-us.apache.org/repos/asf/syncope/blob/f9f50c57/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_pt_BR.properties
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_pt_BR.properties
new file mode 100644
index 0000000..878c519
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/widgets/ProgressesPanel_pt_BR.properties
@@ -0,0 +1,17 @@
+# 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.
+lastUpdateMsg=\u00daltima Atualiza\u00e7\u00e3o

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