[SYNCOPE-1287] Console implementation

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

Branch: refs/heads/master
Commit: 4e781f4a7457248e5e1566a1ca35dbcf5f139287
Parents: f171ed2
Author: Francesco Chicchiriccò <ilgro...@apache.org>
Authored: Wed Apr 4 17:00:53 2018 +0200
Committer: Francesco Chicchiriccò <ilgro...@apache.org>
Committed: Wed Apr 4 17:00:53 2018 +0200

----------------------------------------------------------------------
 .../client/console/approvals/Approval.java      |   8 +-
 .../approvals/ApprovalDirectoryPanel.java       |  40 +-
 .../client/console/approvals/ApprovalModal.java |  11 +-
 .../client/console/commons/Constants.java       |   6 +-
 .../client/console/commons/GroupComparator.java |  37 --
 .../syncope/client/console/pages/BasePage.java  |  24 +-
 .../client/console/pages/Remediations.java      |  42 ++
 .../console/panels/AnyObjectDirectoryPanel.java |   8 +-
 .../client/console/panels/ConnObjects.java      |   6 +-
 .../console/panels/GroupDirectoryPanel.java     |   8 +-
 .../client/console/panels/RealmChoicePanel.java |  26 +-
 .../panels/RemediationDirectoryPanel.java       | 492 +++++++++++++++++++
 .../console/panels/UserDirectoryPanel.java      |   8 +-
 .../client/console/rest/AnyTypeRestClient.java  |   8 +-
 .../client/console/rest/PolicyRestClient.java   |  16 +-
 .../console/rest/RemediationRestClient.java     |  65 +++
 .../console/status/ResourceStatusModal.java     |   6 +-
 .../client/console/widgets/ApprovalsWidget.java |  24 +-
 .../client/console/widgets/JobWidget.java       |   5 +-
 .../console/widgets/RemediationsWidget.java     | 167 +++++++
 .../console/wizards/any/AbstractAttrs.java      |   4 +-
 .../wizards/any/AnyObjectWizardBuilder.java     |  21 +-
 .../console/wizards/any/AnyObjectWrapper.java   |  42 ++
 .../client/console/wizards/any/DerAttrs.java    |  13 +-
 .../console/wizards/any/GroupWizardBuilder.java |  19 +
 .../console/wizards/any/GroupWrapper.java       |  11 +
 .../client/console/wizards/any/PlainAttrs.java  |   9 +-
 .../console/wizards/any/UserWizardBuilder.java  |   2 +-
 .../client/console/wizards/any/VirAttrs.java    |   9 +-
 .../resources/ResourceProvisionPanel.java       |   6 +-
 .../client/console/approvals/ApprovalModal.html |   9 +-
 .../approvals/ApprovalModal_it.properties       |  17 +
 .../approvals/ApprovalModal_pt_BR.properties    |  17 +
 .../approvals/ApprovalModal_ru.properties       |  17 +
 .../syncope/client/console/pages/BasePage.html  |   4 +
 .../client/console/pages/Remediations.html      |  35 ++
 .../console/pages/Remediations.properties       |  27 +
 .../console/pages/Remediations_it.properties    |  27 +
 .../console/pages/Remediations_ja.properties    |  27 +
 .../console/pages/Remediations_pt_BR.properties |  27 +
 .../console/pages/Remediations_ru.properties    |  27 +
 .../panels/UserDirectoryPanel_it.properties     |   2 +-
 .../panels/UserDirectoryPanel_pt_BR.properties  |   2 +-
 .../markup/html/form/ActionPanel_it.properties  |   4 +-
 .../markup/html/form/ActionPanel_ru.properties  |   2 +-
 .../console/widgets/ApprovalsWidget.properties  |   2 +-
 .../widgets/ApprovalsWidget_it.properties       |   2 +-
 .../widgets/ApprovalsWidget_pt_BR.properties    |   2 +-
 .../widgets/RemediationsWidget$InnerPanel.html  |  26 +
 .../widgets/RemediationsWidget.properties       |  18 +
 .../widgets/RemediationsWidget_it.properties    |  18 +
 .../widgets/RemediationsWidget_ja.properties    |  18 +
 .../widgets/RemediationsWidget_pt_BR.properties |  18 +
 .../widgets/RemediationsWidget_ru.properties    |  19 +
 .../syncope/common/lib/to/RemediationTO.java    |  11 +-
 .../syncope/core/logic/RemediationLogic.java    |   6 +-
 .../persistence/api/dao/RemediationDAO.java     |   3 +
 .../persistence/api/entity/Remediation.java     |   5 +-
 .../core/persistence/jpa/dao/JPAAnyTypeDAO.java |  10 +
 .../persistence/jpa/dao/JPARemediationDAO.java  |  10 +
 .../persistence/jpa/entity/JPARemediation.java  |  16 +-
 .../validation/entity/RemediationValidator.java |   8 +-
 .../persistence/jpa/inner/RemediationTest.java  |   7 +-
 .../java/data/RemediationDataBinderImpl.java    |   8 +-
 .../pushpull/AbstractPullResultHandler.java     |  11 +-
 .../cxf/service/RemediationServiceImpl.java     |  11 +-
 .../apache/syncope/fit/core/PullTaskITCase.java |   2 +-
 67 files changed, 1386 insertions(+), 232 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
index 18bf9a3..c34fb00 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
@@ -184,23 +184,23 @@ public abstract class Approval extends Panel {
             }
         };
 
-        final AjaxLink<String> userDetails = new 
AjaxLink<String>("userDetails") {
+        AjaxLink<String> userDetails = new AjaxLink<String>("userDetails") {
 
             private static final long serialVersionUID = -4804368561204623354L;
 
             @Override
             public void onClick(final AjaxRequestTarget target) {
-                viewDetails(formTO, target);
+                viewDetails(target);
             }
         };
         MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, 
StandardEntitlement.USER_READ);
 
-        final boolean enabled = formTO.getUserTO() != null;
+        boolean enabled = formTO.getUserTO() != null;
         userDetails.setVisible(enabled).setEnabled(enabled);
 
         add(propView);
         add(userDetails);
     }
 
-    protected abstract void viewDetails(final WorkflowFormTO formTO, final 
AjaxRequestTarget target);
+    protected abstract void viewDetails(final AjaxRequestTarget target);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
index b694ee1..60077ba 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDirectoryPanel.java
@@ -115,8 +115,8 @@ public class ApprovalDirectoryPanel
 
     @Override
     protected List<IColumn<WorkflowFormTO, String>> getColumns() {
-
         List<IColumn<WorkflowFormTO, String>> columns = new ArrayList<>();
+
         columns.add(new PropertyColumn<>(
                 new ResourceModel("taskId"), "taskId", "taskId"));
         columns.add(new PropertyColumn<>(
@@ -155,11 +155,10 @@ public class ApprovalDirectoryPanel
 
             @Override
             public void onClick(final AjaxRequestTarget target, final 
WorkflowFormTO ignore) {
-                final IModel<WorkflowFormTO> formModel = new 
CompoundPropertyModel<>(model.getObject());
-                manageApprovalModal.setFormModel(formModel);
+                manageApprovalModal.setFormModel(new 
CompoundPropertyModel<>(model.getObject()));
 
-                target.add(manageApprovalModal.setContent(new 
ApprovalModal(manageApprovalModal, pageRef, model.
-                        getObject()) {
+                target.add(manageApprovalModal.setContent(
+                        new ApprovalModal(manageApprovalModal, pageRef, 
model.getObject()) {
 
                     private static final long serialVersionUID = 
5546519445061007248L;
 
@@ -196,12 +195,11 @@ public class ApprovalDirectoryPanel
 
             @Override
             public void onClick(final AjaxRequestTarget target, final 
WorkflowFormTO ignore) {
-                final IModel<WorkflowFormTO> formModel = new 
CompoundPropertyModel<>(model.getObject());
-                modal.setFormModel(formModel);
+                modal.setFormModel(new 
CompoundPropertyModel<>(model.getObject()));
 
-                final WorkflowFormTO formTO = formModel.getObject();
-                final UserTO newUserTO;
-                final UserTO previousUserTO;
+                WorkflowFormTO formTO = model.getObject();
+                UserTO newUserTO;
+                UserTO previousUserTO;
                 if (formTO.getUserPatch() == null) {
                     newUserTO = formTO.getUserTO();
                     previousUserTO = null;
@@ -219,16 +217,14 @@ public class ApprovalDirectoryPanel
 
                 AjaxWizard.EditItemActionEvent<UserTO> editItemActionEvent =
                         new AjaxWizard.EditItemActionEvent<>(newUserTO, 
target);
-
-                editItemActionEvent.forceModalPanel(
-                        new ApprovalUserWizardBuilder(
-                                formModel.getObject(),
-                                previousUserTO,
-                                newUserTO,
-                                new 
AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
-                                
FormLayoutInfoUtils.fetch(Collections.singletonList(AnyTypeKind.USER.name())).getLeft(),
-                                pageRef
-                        ).build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT));
+                editItemActionEvent.forceModalPanel(new 
ApprovalUserWizardBuilder(
+                        model.getObject(),
+                        previousUserTO,
+                        newUserTO,
+                        new 
AnyTypeRestClient().read(AnyTypeKind.USER.name()).getClasses(),
+                        
FormLayoutInfoUtils.fetch(Collections.singletonList(AnyTypeKind.USER.name())).getLeft(),
+                        pageRef
+                ).build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT));
 
                 send(ApprovalDirectoryPanel.this, Broadcast.EXACT, 
editItemActionEvent);
             }
@@ -281,14 +277,14 @@ public class ApprovalDirectoryPanel
         }
 
         @Override
-        public IModel<WorkflowFormTO> model(final WorkflowFormTO 
configuration) {
+        public IModel<WorkflowFormTO> model(final WorkflowFormTO form) {
             return new AbstractReadOnlyModel<WorkflowFormTO>() {
 
                 private static final long serialVersionUID = 
-2566070996511906708L;
 
                 @Override
                 public WorkflowFormTO getObject() {
-                    return configuration;
+                    return form;
                 }
             };
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
index 4a75c06..16048dd 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
@@ -36,6 +36,8 @@ public class ApprovalModal extends Panel implements 
SubmitableModalPanel, Wizard
 
     private static final long serialVersionUID = -8847854414429745216L;
 
+    private final UserWorkflowRestClient restClient = new 
UserWorkflowRestClient();
+
     private final BaseModal<?> modal;
 
     private final WorkflowFormTO formTO;
@@ -48,23 +50,22 @@ public class ApprovalModal extends Panel implements 
SubmitableModalPanel, Wizard
         this.formTO = formTO;
         this.pageRef = pageRef;
 
-        final MultilevelPanel mlp = new MultilevelPanel("approval");
-        add(mlp);
-
+        MultilevelPanel mlp = new MultilevelPanel("approval");
         mlp.setFirstLevel(new Approval(pageRef, formTO) {
 
             private static final long serialVersionUID = -2195387360323687302L;
 
             @Override
-            protected void viewDetails(final WorkflowFormTO formTO, final 
AjaxRequestTarget target) {
+            protected void viewDetails(final AjaxRequestTarget target) {
                 mlp.next(getString("approval.details"), new 
ApprovalDetails(pageRef, formTO), target);
             }
         });
+        add(mlp);
     }
 
     @Override
     public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-        new UserWorkflowRestClient().submitForm(formTO);
+        this.restClient.submitForm(formTO);
         this.modal.show(false);
         this.modal.close(target);
         
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index e0f70c4..31cbda1 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -65,6 +65,8 @@ public final class Constants {
 
     public static final String SEARCH_ERROR = "search_error";
 
+    public static final String UNEXPECTED_CONDITION_ERROR = 
"unexpected_condition_error";
+
     public static final String ERROR = "error";
 
     public static final String BEFORE_LOGOUT_PAGE = "beforeLogoutPage";
@@ -117,7 +119,9 @@ public final class Constants {
 
     public static final String PREF_ACCESS_TOKEN_PAGINATOR_ROWS = 
"accessToken.paginator.rows";
 
-    public static final String PREF_WORKFLOW_FORM_PAGINATOR_ROWS = 
"role.paginator.workflow.form";
+    public static final String PREF_WORKFLOW_FORM_PAGINATOR_ROWS = 
"workflow.paginator.rows";
+
+    public static final String PREF_REMEDIATION_PAGINATOR_ROWS = 
"remediation.paginator.rows";
 
     public static final String PREF_RESOURCES_PAGINATOR_ROWS = 
"resources.paginator.rows";
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/commons/GroupComparator.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/commons/GroupComparator.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/commons/GroupComparator.java
deleted file mode 100644
index b45633f..0000000
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/commons/GroupComparator.java
+++ /dev/null
@@ -1,37 +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.commons;
-
-import java.io.Serializable;
-import java.util.Comparator;
-import org.apache.syncope.common.lib.to.GroupTO;
-
-public class GroupComparator implements Comparator<GroupTO>, Serializable {
-
-    private static final long serialVersionUID = 3584905855352863080L;
-
-    @Override
-    public int compare(final GroupTO left, final GroupTO right) {
-        return left == null || left.getName() == null
-                ? -1
-                : right == null || right.getName() == null
-                ? 1
-                : left.getName().compareTo(right.getName());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
index 9aa8fa1..93f385d 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
@@ -35,6 +35,7 @@ import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.client.console.wicket.markup.head.MetaHeaderItem;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import org.apache.syncope.client.console.widgets.ApprovalsWidget;
+import org.apache.syncope.client.console.widgets.RemediationsWidget;
 import org.apache.syncope.common.lib.info.PlatformInfo;
 import org.apache.syncope.common.lib.info.SystemInfo;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
@@ -82,6 +83,8 @@ public class BasePage extends WebPage implements 
IAjaxIndicatorAware {
 
     protected ApprovalsWidget approvalsWidget;
 
+    protected RemediationsWidget remediationWidget;
+
     public BasePage() {
         this(null);
     }
@@ -102,6 +105,9 @@ public class BasePage extends WebPage implements 
IAjaxIndicatorAware {
         // header, footer
         body.add(new Label("username", 
SyncopeConsoleSession.get().getSelfTO().getUsername()));
 
+        remediationWidget = new RemediationsWidget("remediationWidget", 
getPageReference());
+        body.add(remediationWidget.setRenderBodyOnly(true));
+
         approvalsWidget = new ApprovalsWidget("approvalsWidget", 
getPageReference());
         body.add(approvalsWidget.setRenderBodyOnly(true));
 
@@ -164,10 +170,9 @@ public class BasePage extends WebPage implements 
IAjaxIndicatorAware {
         liContainer = new WebMarkupContainer(getLIContainerId("topology"));
         body.add(liContainer);
         link = BookmarkablePageLinkBuilder.build("topology", Topology.class);
-        StringBuilder bld = new StringBuilder();
-        bld.append(StandardEntitlement.CONNECTOR_LIST).append(",").
-                append(StandardEntitlement.RESOURCE_LIST).append(",");
-        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, 
bld.toString());
+        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER,
+                StringUtils.join(
+                        new String[] { StandardEntitlement.CONNECTOR_LIST, 
StandardEntitlement.CONNECTOR_LIST }, ","));
         liContainer.add(link);
 
         liContainer = new WebMarkupContainer(getLIContainerId("reports"));
@@ -216,10 +221,9 @@ public class BasePage extends WebPage implements 
IAjaxIndicatorAware {
         liContainer = new WebMarkupContainer(getLIContainerId("security"));
         confULContainer.add(liContainer);
         link = BookmarkablePageLinkBuilder.build("security", Security.class);
-        bld = new StringBuilder();
-        bld.append(StandardEntitlement.ROLE_LIST).append(",").
-                append(StandardEntitlement.APPLICATION_LIST);
-        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER, 
bld.toString());
+        MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER,
+                StringUtils.join(
+                        new String[] { StandardEntitlement.ROLE_LIST, 
StandardEntitlement.APPLICATION_LIST }, ","));
         liContainer.add(link);
 
         liContainer = new WebMarkupContainer(getLIContainerId("policies"));
@@ -421,6 +425,10 @@ public class BasePage extends WebPage implements 
IAjaxIndicatorAware {
         return notificationPanel;
     }
 
+    public RemediationsWidget getRemediationWidget() {
+        return remediationWidget;
+    }
+
     public ApprovalsWidget getApprovalsWidget() {
         return approvalsWidget;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java
new file mode 100644
index 0000000..8e539d5
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/pages/Remediations.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pages;
+
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.panels.RemediationDirectoryPanel;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class Remediations extends BasePage {
+
+    private static final long serialVersionUID = -7940154016753661388L;
+
+    public Remediations(final PageParameters parameters) {
+        super(parameters);
+
+        body.add(BookmarkablePageLinkBuilder.build("dashboard", "dashboardBr", 
Dashboard.class));
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        body.add(content);
+
+        content.add(new RemediationDirectoryPanel("remediations", 
getPageReference()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
index 1c2cc52..3db9615 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyObjectDirectoryPanel.java
@@ -109,8 +109,9 @@ public class AnyObjectDirectoryPanel extends 
AnyDirectoryPanel<AnyObjectTO, AnyO
                                 new AnyWrapper<>(new 
AnyObjectRestClient().read(model.getObject().getKey())),
                                 target));
             }
-        }, ActionType.EDIT, new 
StringBuilder().append(AnyEntitlement.READ.getFor(type)).append(",").
-                
append(AnyEntitlement.UPDATE.getFor(type)).toString()).setRealm(realm);
+        }, ActionType.EDIT, StringUtils.join(
+                new String[] { AnyEntitlement.READ.getFor(type), 
AnyEntitlement.UPDATE.getFor(type) }, ",")).
+                setRealm(realm);
 
         panel.add(new ActionLink<AnyObjectTO>() {
 
@@ -153,8 +154,7 @@ public class AnyObjectDirectoryPanel extends 
AnyDirectoryPanel<AnyObjectTO, AnyO
 
                     altDefaultModal.show(true);
                 }
-            }, ActionType.MANAGE_RESOURCES, new 
StringBuilder().append(AnyEntitlement.READ.getFor(type)).append(",").
-                    
append(AnyEntitlement.UPDATE.getFor(type)).toString()).setRealm(realm);
+            }, ActionType.MANAGE_RESOURCES, 
AnyEntitlement.UPDATE.getFor(type)).setRealm(realm);
 
             panel.add(
                     new ActionLink<AnyObjectTO>() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
index fc8318d..303cbf1 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.commons.Constants;
@@ -49,8 +48,9 @@ public class ConnObjects extends Panel implements ModalPanel {
         super(BaseModal.CONTENT_ID);
 
         List<String> availableAnyTypes = resource.getProvisions().stream().
-                map(ProvisionTO::getAnyType).collect(Collectors.toList());
-        Collections.sort(availableAnyTypes, new 
AnyTypeRestClient.AnyTypeKeyComparator());
+                map(ProvisionTO::getAnyType).
+                sorted(AnyTypeRestClient.KEY_COMPARATOR).
+                collect(Collectors.toList());
         if (resource.getOrgUnit() != null) {
             availableAnyTypes.add(0, SyncopeConstants.REALM_ANYTYPE);
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
index 0c36d43..6168829 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/GroupDirectoryPanel.java
@@ -214,8 +214,9 @@ public class GroupDirectoryPanel extends 
AnyDirectoryPanel<GroupTO, GroupRestCli
                         new AjaxWizard.EditItemActionEvent<>(new GroupWrapper(
                                 restClient.read(model.getObject().getKey())), 
target));
             }
-        }, ActionType.EDIT, new 
StringBuilder().append(StandardEntitlement.GROUP_READ).append(",").
-                
append(StandardEntitlement.GROUP_UPDATE).toString()).setRealm(realm);
+        }, ActionType.EDIT, StringUtils.join(
+                new String[] { StandardEntitlement.GROUP_READ, 
StandardEntitlement.GROUP_UPDATE }, ",")).
+                setRealm(realm);
 
         panel.add(new ActionLink<GroupTO>() {
 
@@ -262,8 +263,7 @@ public class GroupDirectoryPanel extends 
AnyDirectoryPanel<GroupTO, GroupRestCli
             public boolean isIndicatorEnabled() {
                 return false;
             }
-        }, ActionType.MEMBERS, new 
StringBuilder().append(StandardEntitlement.GROUP_READ).append(",").
-                
append(StandardEntitlement.GROUP_UPDATE).toString()).setRealm(realm);
+        }, ActionType.MEMBERS, 
StandardEntitlement.GROUP_UPDATE).setRealm(realm);
 
         panel.add(new ActionLink<GroupTO>() {
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
index 7e74241..d4bae1a 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
@@ -25,7 +25,6 @@ import 
de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 import 
de.agilecoders.wicket.core.markup.html.bootstrap.button.dropdown.DropDownButton;
 import de.agilecoders.wicket.core.markup.html.bootstrap.image.GlyphIconType;
 import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType;
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -33,6 +32,7 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -307,9 +307,9 @@ public class RealmChoicePanel extends Panel {
     }
 
     private Map<String, Pair<RealmTO, List<RealmTO>>> reloadRealmParentMap() {
-        final List<RealmTO> realms = realmRestClient.list();
-        Collections.sort(realms, new RealmNameComparator());
-        return reloadRealmParentMap(realms);
+        return reloadRealmParentMap(realmRestClient.list().stream().
+                sorted(Comparator.comparing(RealmTO::getName)).
+                collect(Collectors.toList()));
     }
 
     private Map<String, Pair<RealmTO, List<RealmTO>>> 
reloadRealmParentMap(final List<RealmTO> realms) {
@@ -339,24 +339,6 @@ public class RealmChoicePanel extends Panel {
         return tree;
     }
 
-    private static class RealmNameComparator implements Comparator<RealmTO>, 
Serializable {
-
-        private static final long serialVersionUID = 7085057398406518811L;
-
-        @Override
-        public int compare(final RealmTO r1, final RealmTO r2) {
-            if (r1 == null && r2 == null) {
-                return 0;
-            } else if (r1 != null && r2 != null) {
-                return r1.getName().compareTo(r2.getName());
-            } else if (r1 == null && r2 != null) {
-                return -1;
-            } else {
-                return 1;
-            }
-        }
-    }
-
     /**
      * Gets current selected realm.
      *

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java
new file mode 100644
index 0000000..691ac5c
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/RemediationDirectoryPanel.java
@@ -0,0 +1,492 @@
+/*
+ * 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.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+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.SyncopeConsoleSession;
+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.layout.AnyObjectFormLayoutInfo;
+import org.apache.syncope.client.console.layout.FormLayoutInfoUtils;
+import org.apache.syncope.client.console.layout.GroupFormLayoutInfo;
+import org.apache.syncope.client.console.layout.UserFormLayoutInfo;
+import org.apache.syncope.client.console.pages.BasePage;
+import 
org.apache.syncope.client.console.panels.RemediationDirectoryPanel.RemediationProvider;
+import org.apache.syncope.client.console.rest.AnyObjectRestClient;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.GroupRestClient;
+import org.apache.syncope.client.console.rest.RemediationRestClient;
+import org.apache.syncope.client.console.rest.UserRestClient;
+import 
org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import 
org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.any.AnyObjectWizardBuilder;
+import org.apache.syncope.client.console.wizards.any.AnyWrapper;
+import org.apache.syncope.client.console.wizards.any.GroupWizardBuilder;
+import org.apache.syncope.client.console.wizards.any.UserWizardBuilder;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.patch.PasswordPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.RemediationTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyEntitlement;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+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.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class RemediationDirectoryPanel
+        extends DirectoryPanel<RemediationTO, RemediationTO, 
RemediationProvider, RemediationRestClient> {
+
+    private static final long serialVersionUID = 8525204188127106587L;
+
+    public RemediationDirectoryPanel(final String id, final PageReference 
pageReference) {
+        super(id, pageReference, true);
+        disableCheckBoxes();
+        setFooterVisibility(false);
+        modal.size(Modal.Size.Large);
+
+        restClient = new RemediationRestClient();
+
+        initResultTable();
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, 
StandardEntitlement.REMEDIATION_REMEDY);
+    }
+
+    @Override
+    protected List<IColumn<RemediationTO, String>> getColumns() {
+        List<IColumn<RemediationTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<>(
+                new StringResourceModel("key", this), "key"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("operation"), "operation", "operation"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("anyType"), "anyType", "anyType"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("remoteName"), "remoteName", "remoteName"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("resource"), "resource", "resource"));
+        columns.add(new DatePropertyColumn<>(
+                new ResourceModel("instant"), "instant", "instant"));
+
+        return columns;
+    }
+
+    @Override
+    protected ActionsPanel<RemediationTO> getActions(final 
IModel<RemediationTO> model) {
+        ActionsPanel<RemediationTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<RemediationTO>() {
+
+            private static final long serialVersionUID = 6193210574968203299L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final 
RemediationTO ignore) {
+                modal.header(new ResourceModel("error"));
+                modal.setContent(new 
ExecMessageModal(model.getObject().getError()));
+                modal.show(true);
+                target.add(modal);
+            }
+        }, ActionLink.ActionType.VIEW_DETAILS, 
StandardEntitlement.REMEDIATION_READ);
+
+        if (model.getObject().getOperation() == ResourceOperation.DELETE) {
+            String entitlements = StringUtils.join(new String[] {
+                StandardEntitlement.REMEDIATION_REMEDY,
+                AnyTypeKind.USER.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.USER_DELETE
+                : 
AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.GROUP_DELETE
+                : AnyEntitlement.DELETE.getFor(model.getObject().getAnyType()) 
}, ",");
+
+            panel.add(new ActionLink<RemediationTO>() {
+
+                private static final long serialVersionUID = 
6193210574968203299L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target, final 
RemediationTO ignore) {
+                    try {
+                        restClient.remedy(model.getObject().getKey(), 
model.getObject().getKeyPayload());
+                        
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                        target.add(container);
+                    } catch (SyncopeClientException e) {
+                        LOG.error("While performing remediation {}", 
model.getObject().getKey(), e);
+                        
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                                ? e.getClass().getName() : e.getMessage());
+                    }
+                    ((BasePage) 
pageRef.getPage()).getNotificationPanel().refresh(target);
+                }
+            }, ActionLink.ActionType.CLOSE, entitlements, true);
+        } else {
+            String entitlements = model.getObject().getOperation() == 
ResourceOperation.CREATE
+                    ? StringUtils.join(new String[] {
+                StandardEntitlement.REMEDIATION_REMEDY,
+                AnyTypeKind.USER.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.USER_CREATE
+                : 
AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.GROUP_CREATE
+                : AnyEntitlement.CREATE.getFor(model.getObject().getAnyType()) 
}, ",")
+                    : StringUtils.join(new String[] {
+                StandardEntitlement.REMEDIATION_REMEDY,
+                AnyTypeKind.USER.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.USER_UPDATE
+                : 
AnyTypeKind.GROUP.name().equals(model.getObject().getAnyType())
+                ? StandardEntitlement.GROUP_UPDATE
+                : AnyEntitlement.UPDATE.getFor(model.getObject().getAnyType()) 
}, ",");
+
+            panel.add(new ActionLink<RemediationTO>() {
+
+                private static final long serialVersionUID = 
6193210574968203299L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target, final 
RemediationTO ignore) {
+                    modal.setFormModel(new 
CompoundPropertyModel<>(model.getObject()));
+                    RemediationTO remediationTO = model.getObject();
+
+                    switch (remediationTO.getAnyType()) {
+                        case "USER":
+                            UserTO newUserTO;
+                            UserTO previousUserTO;
+                            if (remediationTO.getAnyPatchPayload() == null) {
+                                newUserTO = (UserTO) 
remediationTO.getAnyTOPayload();
+                                previousUserTO = null;
+                            } else {
+                                previousUserTO = new UserRestClient().
+                                        
read(remediationTO.getAnyPatchPayload().getKey());
+                                newUserTO = AnyOperations.patch(
+                                        previousUserTO, (UserPatch) 
remediationTO.getAnyPatchPayload());
+                            }
+
+                            AjaxWizard.EditItemActionEvent<UserTO> userEvent =
+                                    new 
AjaxWizard.EditItemActionEvent<>(newUserTO, target);
+                            userEvent.forceModalPanel(new 
RemediationUserWizardBuilder(
+                                    model.getObject(),
+                                    previousUserTO,
+                                    newUserTO,
+                                    new 
AnyTypeRestClient().read(remediationTO.getAnyType()).getClasses(),
+                                    
FormLayoutInfoUtils.fetch(Arrays.asList(remediationTO.getAnyType())).getLeft(),
+                                    pageRef
+                            ).build(BaseModal.CONTENT_ID, 
AjaxWizard.Mode.EDIT));
+                            send(RemediationDirectoryPanel.this, 
Broadcast.EXACT, userEvent);
+                            break;
+
+                        case "GROUP":
+                            GroupTO newGroupTO;
+                            GroupTO previousGroupTO;
+                            if (remediationTO.getAnyPatchPayload() == null) {
+                                newGroupTO = (GroupTO) 
remediationTO.getAnyTOPayload();
+                                previousGroupTO = null;
+                            } else {
+                                previousGroupTO = new GroupRestClient().
+                                        
read(remediationTO.getAnyPatchPayload().getKey());
+                                newGroupTO = AnyOperations.patch(
+                                        previousGroupTO, (GroupPatch) 
remediationTO.getAnyPatchPayload());
+                            }
+
+                            AjaxWizard.EditItemActionEvent<GroupTO> groupEvent 
=
+                                    new 
AjaxWizard.EditItemActionEvent<>(newGroupTO, target);
+                            groupEvent.forceModalPanel(new 
RemediationGroupWizardBuilder(
+                                    model.getObject(),
+                                    previousGroupTO,
+                                    newGroupTO,
+                                    new 
AnyTypeRestClient().read(remediationTO.getAnyType()).getClasses(),
+                                    
FormLayoutInfoUtils.fetch(Arrays.asList(remediationTO.getAnyType())).getMiddle(),
+                                    pageRef
+                            ).build(BaseModal.CONTENT_ID, 
AjaxWizard.Mode.EDIT));
+                            send(RemediationDirectoryPanel.this, 
Broadcast.EXACT, groupEvent);
+                            break;
+
+                        default:
+                            AnyObjectTO newAnyObjectTO;
+                            AnyObjectTO previousAnyObjectTO;
+                            if (remediationTO.getAnyPatchPayload() == null) {
+                                newAnyObjectTO = (AnyObjectTO) 
remediationTO.getAnyTOPayload();
+                                previousAnyObjectTO = null;
+                            } else {
+                                previousAnyObjectTO = new 
AnyObjectRestClient().
+                                        
read(remediationTO.getAnyPatchPayload().getKey());
+                                newAnyObjectTO = AnyOperations.patch(
+                                        previousAnyObjectTO, (AnyObjectPatch) 
remediationTO.getAnyPatchPayload());
+                            }
+
+                            AjaxWizard.EditItemActionEvent<AnyObjectTO> 
anyObjectEvent =
+                                    new 
AjaxWizard.EditItemActionEvent<>(newAnyObjectTO, target);
+                            anyObjectEvent.forceModalPanel(new 
RemediationAnyObjectWizardBuilder(
+                                    model.getObject(),
+                                    previousAnyObjectTO,
+                                    newAnyObjectTO,
+                                    new 
AnyTypeRestClient().read(remediationTO.getAnyType()).getClasses(),
+                                    
FormLayoutInfoUtils.fetch(Arrays.asList(remediationTO.getAnyType())).
+                                            
getRight().values().iterator().next(),
+                                    pageRef
+                            ).build(BaseModal.CONTENT_ID, 
AjaxWizard.Mode.EDIT));
+                            send(RemediationDirectoryPanel.this, 
Broadcast.EXACT, anyObjectEvent);
+                    }
+                }
+            }, ActionLink.ActionType.EDIT, entitlements);
+        }
+
+        panel.add(new ActionLink<RemediationTO>() {
+
+            private static final long serialVersionUID = 6193210574968203299L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final 
RemediationTO ignore) {
+                try {
+                    restClient.delete(model.getObject().getKey());
+                    
SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+                    target.add(container);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", model.getObject().getKey(), 
e);
+                    
SyncopeConsoleSession.get().error(StringUtils.isBlank(e.getMessage())
+                            ? e.getClass().getName() : e.getMessage());
+                }
+                ((BasePage) 
pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, 
StandardEntitlement.REMEDIATION_DELETE, true);
+
+        return panel;
+    }
+
+    @Override
+    protected RemediationProvider dataProvider() {
+        return new RemediationProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_REMEDIATION_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+
+    public static class RemediationProvider extends 
DirectoryDataProvider<RemediationTO> {
+
+        private static final long serialVersionUID = -2311716167583335852L;
+
+        private final SortableDataProviderComparator<RemediationTO> comparator;
+
+        private final RemediationRestClient restClient = new 
RemediationRestClient();
+
+        public RemediationProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("instant", SortOrder.ASCENDING);
+            this.comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<RemediationTO> iterator(final long first, final long 
count) {
+            final List<RemediationTO> list = restClient.getRemediations();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) 
count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getRemediations().size();
+        }
+
+        @Override
+        public IModel<RemediationTO> model(final RemediationTO remediation) {
+            return new AbstractReadOnlyModel<RemediationTO>() {
+
+                private static final long serialVersionUID = 
-2566070996511906708L;
+
+                @Override
+                public RemediationTO getObject() {
+                    return remediation;
+                }
+            };
+        }
+    }
+
+    private class RemediationUserWizardBuilder extends UserWizardBuilder {
+
+        private static final long serialVersionUID = 6840699724316612700L;
+
+        private final UserTO previousUserTO;
+
+        private final RemediationTO remediationTO;
+
+        RemediationUserWizardBuilder(
+                final RemediationTO remediationTO,
+                final UserTO previousUserTO,
+                final UserTO userTO,
+                final List<String> anyTypeClasses,
+                final UserFormLayoutInfo formLayoutInfo,
+                final PageReference pageRef) {
+
+            super(previousUserTO, userTO, anyTypeClasses, formLayoutInfo, 
pageRef);
+            this.previousUserTO = previousUserTO;
+            this.remediationTO = remediationTO;
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final AnyWrapper<UserTO> 
modelObject) {
+            UserTO inner = modelObject.getInnerObject();
+
+            ProvisioningResult<UserTO> result;
+
+            if (remediationTO.getAnyPatchPayload() == null) {
+                result = restClient.remedy(remediationTO.getKey(), inner);
+            } else {
+                UserPatch patch = AnyOperations.diff(inner, previousUserTO, 
false);
+
+                if (StringUtils.isNotBlank(inner.getPassword())) {
+                    PasswordPatch passwordPatch = new PasswordPatch.Builder().
+                            
value(inner.getPassword()).onSyncope(true).resources(inner.
+                            getResources()).
+                            build();
+                    patch.setPassword(passwordPatch);
+                }
+                // update just if it is changed
+                if (patch.isEmpty()) {
+                    result = new ProvisioningResult<>();
+                    result.setEntity(inner);
+                } else {
+                    result = restClient.remedy(remediationTO.getKey(), patch);
+                }
+            }
+
+            return result;
+        }
+    }
+
+    private class RemediationGroupWizardBuilder extends GroupWizardBuilder {
+
+        private static final long serialVersionUID = -5233791906979150786L;
+
+        private final GroupTO previousGroupTO;
+
+        private final RemediationTO remediationTO;
+
+        RemediationGroupWizardBuilder(
+                final RemediationTO remediationTO,
+                final GroupTO previousGroupTO,
+                final GroupTO groupTO,
+                final List<String> anyTypeClasses,
+                final GroupFormLayoutInfo formLayoutInfo,
+                final PageReference pageRef) {
+
+            super(previousGroupTO, groupTO, anyTypeClasses, formLayoutInfo, 
pageRef);
+            this.previousGroupTO = previousGroupTO;
+            this.remediationTO = remediationTO;
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final AnyWrapper<GroupTO> 
modelObject) {
+            GroupTO inner = modelObject.getInnerObject();
+
+            ProvisioningResult<GroupTO> result;
+
+            if (remediationTO.getAnyPatchPayload() == null) {
+                result = restClient.remedy(remediationTO.getKey(), inner);
+            } else {
+                GroupPatch patch = AnyOperations.diff(inner, previousGroupTO, 
false);
+
+                // update just if it is changed
+                if (patch.isEmpty()) {
+                    result = new ProvisioningResult<>();
+                    result.setEntity(inner);
+                } else {
+                    result = restClient.remedy(remediationTO.getKey(), patch);
+                }
+            }
+
+            return result;
+        }
+    }
+
+    private class RemediationAnyObjectWizardBuilder extends 
AnyObjectWizardBuilder {
+
+        private static final long serialVersionUID = 6993139499479015083L;
+
+        private final AnyObjectTO previousAnyObjectTO;
+
+        private final RemediationTO remediationTO;
+
+        RemediationAnyObjectWizardBuilder(
+                final RemediationTO remediationTO,
+                final AnyObjectTO previousAnyObjectTO,
+                final AnyObjectTO anyObjectTO,
+                final List<String> anyTypeClasses,
+                final AnyObjectFormLayoutInfo formLayoutInfo,
+                final PageReference pageRef) {
+
+            super(previousAnyObjectTO, anyObjectTO, anyTypeClasses, 
formLayoutInfo, pageRef);
+            this.previousAnyObjectTO = previousAnyObjectTO;
+            this.remediationTO = remediationTO;
+        }
+
+        @Override
+        protected Serializable onApplyInternal(final AnyWrapper<AnyObjectTO> 
modelObject) {
+            AnyObjectTO inner = modelObject.getInnerObject();
+
+            ProvisioningResult<AnyObjectTO> result;
+
+            if (remediationTO.getAnyPatchPayload() == null) {
+                result = restClient.remedy(remediationTO.getKey(), inner);
+            } else {
+                AnyObjectPatch patch = AnyOperations.diff(inner, 
previousAnyObjectTO, false);
+
+                // update just if it is changed
+                if (patch.isEmpty()) {
+                    result = new ProvisioningResult<>();
+                    result.setEntity(inner);
+                } else {
+                    result = restClient.remedy(remediationTO.getKey(), patch);
+                }
+            }
+
+            return result;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
index 2455471..66f214b 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDirectoryPanel.java
@@ -141,8 +141,9 @@ public class UserDirectoryPanel extends 
AnyDirectoryPanel<UserTO, UserRestClient
                                 new UserWrapper(new 
UserRestClient().read(model.getObject().getKey())),
                                 target));
             }
-        }, ActionType.EDIT, new 
StringBuilder().append(StandardEntitlement.USER_READ).append(",").
-                
append(StandardEntitlement.USER_UPDATE).toString()).setRealm(realm);
+        }, ActionType.EDIT, StringUtils.join(
+                new String[] { StandardEntitlement.USER_READ, 
StandardEntitlement.USER_UPDATE }, ",")).
+                setRealm(realm);
 
         panel.add(new ActionLink<UserTO>() {
 
@@ -207,8 +208,7 @@ public class UserDirectoryPanel extends 
AnyDirectoryPanel<UserTO, UserRestClient
 
                     displayAttributeModal.show(true);
                 }
-            }, ActionType.PASSWORD_MANAGEMENT,
-                    new 
StringBuilder().append(StandardEntitlement.USER_UPDATE).toString()).setRealm(realm);
+            }, ActionType.PASSWORD_MANAGEMENT, 
StandardEntitlement.USER_UPDATE).setRealm(realm);
 
             if 
(SyncopeConsoleSession.get().getPlatformInfo().isPwdResetAllowed()
                     && 
!SyncopeConsoleSession.get().getPlatformInfo().isPwdResetRequiringSecurityQuestions())
 {

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyTypeRestClient.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyTypeRestClient.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyTypeRestClient.java
index 5db9f07..b1e55a6 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyTypeRestClient.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/AnyTypeRestClient.java
@@ -33,6 +33,10 @@ public class AnyTypeRestClient extends BaseRestClient {
 
     private static final long serialVersionUID = -2211371717449597247L;
 
+    private static final AnyTypeComparator COMPARATOR = new 
AnyTypeComparator();
+
+    public static final Comparator<String> KEY_COMPARATOR = new 
AnyTypeKeyComparator();
+
     public AnyTypeTO read(final String key) {
         AnyTypeTO type = null;
 
@@ -76,7 +80,7 @@ public class AnyTypeRestClient extends BaseRestClient {
         getService(AnyTypeService.class).delete(key);
     }
 
-    public static class AnyTypeComparator implements Comparator<AnyTypeTO>, 
Serializable {
+    private static class AnyTypeComparator implements Comparator<AnyTypeTO>, 
Serializable {
 
         private static final long serialVersionUID = -8227715253094467138L;
 
@@ -98,7 +102,7 @@ public class AnyTypeRestClient extends BaseRestClient {
         }
     }
 
-    public static class AnyTypeKeyComparator implements Comparator<String>, 
Serializable {
+    private static class AnyTypeKeyComparator implements Comparator<String>, 
Serializable {
 
         private static final long serialVersionUID = -7778622183107320760L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
index c6446c8..1440795 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
@@ -19,10 +19,10 @@
 package org.apache.syncope.client.console.rest;
 
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.rest.api.service.PolicyService;
@@ -34,6 +34,8 @@ public class PolicyRestClient extends BaseRestClient {
 
     private static final long serialVersionUID = -1392090291817187902L;
 
+    private static final PolicyComparator COMPARATOR = new PolicyComparator();
+
     public <T extends PolicyTO> T getPolicy(final PolicyType type, final 
String key) {
         T policy = null;
         try {
@@ -46,16 +48,14 @@ public class PolicyRestClient extends BaseRestClient {
 
     @SuppressWarnings("unchecked")
     public <T extends PolicyTO> List<T> getPolicies(final PolicyType type) {
-        final List<T> res = new ArrayList<>();
-
         try {
-            res.addAll((List<T>) getService(PolicyService.class).list(type));
-            Collections.sort(res, new PolicyComparator());
+            return getService(PolicyService.class).<T>list(type).stream().
+                    sorted(COMPARATOR).
+                    collect(Collectors.toList());
         } catch (Exception ignore) {
             LOG.debug("No policy found", ignore);
+            return Collections.<T>emptyList();
         }
-
-        return res;
     }
 
     public <T extends PolicyTO> void createPolicy(final PolicyType type, final 
T policy) {
@@ -70,7 +70,7 @@ public class PolicyRestClient extends BaseRestClient {
         getService(PolicyService.class).delete(type, key);
     }
 
-    private class PolicyComparator implements Comparator<PolicyTO>, 
Serializable {
+    private static class PolicyComparator implements Comparator<PolicyTO>, 
Serializable {
 
         private static final long serialVersionUID = -4921433085213223115L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/rest/RemediationRestClient.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/rest/RemediationRestClient.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/RemediationRestClient.java
new file mode 100644
index 0000000..4dd9ca6
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/rest/RemediationRestClient.java
@@ -0,0 +1,65 @@
+/*
+ * 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.rest;
+
+import static org.apache.syncope.client.console.rest.BaseRestClient.getService;
+
+import java.util.List;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.RemediationTO;
+import org.apache.syncope.common.rest.api.service.RemediationService;
+
+public class RemediationRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -7033745375669316378L;
+
+    public List<RemediationTO> getRemediations() {
+        return getService(RemediationService.class).list();
+    }
+
+    public RemediationTO getRemediation(final String key) {
+        return getService(RemediationService.class).read(key);
+    }
+
+    public <T extends AnyTO> ProvisioningResult<T> remedy(final String key, 
final T anyTO) {
+        Response response = getService(RemediationService.class).remedy(key, 
anyTO);
+        return response.readEntity(new GenericType<ProvisioningResult<T>>() {
+        });
+    }
+
+    public <T extends AnyTO> ProvisioningResult<T> remedy(final String key, 
final AnyPatch anyPatch) {
+        Response response = getService(RemediationService.class).remedy(key, 
anyPatch);
+        return response.readEntity(new GenericType<ProvisioningResult<T>>() {
+        });
+    }
+
+    public ProvisioningResult<? extends AnyTO> remedy(final String key, final 
String anyKey) {
+        Response response = getService(RemediationService.class).remedy(key, 
anyKey);
+        return response.readEntity(new GenericType<ProvisioningResult<? 
extends AnyTO>>() {
+        });
+    }
+
+    public void delete(final String remediation) {
+        getService(RemediationService.class).delete(remediation);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusModal.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusModal.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusModal.java
index 25badd6..ff974e2 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusModal.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/status/ResourceStatusModal.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.status;
 
-import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.commons.Constants;
@@ -51,8 +50,9 @@ public class ResourceStatusModal extends 
StatusModal<ResourceTO> {
         super(baseModal, pageReference, resource, null, false);
 
         List<String> availableAnyTypes = resource.getProvisions().stream().
-                map(ProvisionTO::getAnyType).collect(Collectors.toList());
-        Collections.sort(availableAnyTypes, new 
AnyTypeRestClient.AnyTypeKeyComparator());
+                map(ProvisionTO::getAnyType).
+                sorted(AnyTypeRestClient.KEY_COMPARATOR).
+                collect(Collectors.toList());
 
         AjaxDropDownChoicePanel<String> anyTypes =
                 new AjaxDropDownChoicePanel<>("anyTypes", "anyTypes", 
typeModel, false);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
index cc41e84..9f8ac62 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
@@ -20,11 +20,11 @@ package org.apache.syncope.client.console.widgets;
 
 import de.agilecoders.wicket.core.markup.html.bootstrap.image.Icon;
 import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeIconTypeBuilder;
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
@@ -103,8 +103,9 @@ public class ApprovalsWidget extends 
AlertWidget<WorkflowFormTO> {
                 if 
(SyncopeConsoleSession.get().owns(StandardEntitlement.WORKFLOW_FORM_LIST)
                         && 
SyncopeConsoleSession.get().owns(StandardEntitlement.WORKFLOW_FORM_READ)) {
 
-                    updatedApprovals = restClient.getForms();
-                    Collections.sort(updatedApprovals, new 
WorkflowFormComparator());
+                    updatedApprovals = restClient.getForms().stream().
+                            
sorted(Comparator.comparing(WorkflowFormTO::getCreateTime)).
+                            collect(Collectors.toList());
                 } else {
                     updatedApprovals = Collections.<WorkflowFormTO>emptyList();
                 }
@@ -183,21 +184,4 @@ public class ApprovalsWidget extends 
AlertWidget<WorkflowFormTO> {
         }
 
     }
-
-    private static class WorkflowFormComparator implements 
Comparator<WorkflowFormTO>, Serializable {
-
-        private static final long serialVersionUID = 4650217602780789075L;
-
-        @Override
-        public int compare(final WorkflowFormTO o1, final WorkflowFormTO o2) {
-            if (o1 == null) {
-                return o2 == null ? 0 : 1;
-            } else if (o2 == null) {
-                return -1;
-            } else {
-                // inverse
-                return o2.getCreateTime().compareTo(o1.getCreateTime());
-            }
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
index 8575079..897ae2e 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
@@ -643,10 +643,7 @@ public class JobWidget extends BaseWidget {
 
                 @Override
                 public void onClick(final AjaxRequestTarget target, final 
ExecTO ignore) {
-
-                    StringResourceModel stringResourceModel = new 
StringResourceModel("execution.view", JobWidget.this,
-                            model);
-                    detailModal.header(stringResourceModel);
+                    detailModal.header(new 
StringResourceModel("execution.view", JobWidget.this, model));
                     detailModal.setContent(new 
ExecMessageModal(model.getObject().getMessage()));
                     detailModal.show(true);
                     target.add(detailModal);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java
new file mode 100644
index 0000000..287fdbc
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/widgets/RemediationsWidget.java
@@ -0,0 +1,167 @@
+/*
+ * 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.image.Icon;
+import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.icon.FontAwesomeIconTypeBuilder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.Remediations;
+import org.apache.syncope.client.console.rest.RemediationRestClient;
+import 
org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior;
+import org.apache.syncope.common.lib.to.RemediationTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import 
org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.util.time.Duration;
+
+public class RemediationsWidget extends AlertWidget<RemediationTO> {
+
+    private static final long serialVersionUID = 1817429725840355068L;
+
+    private final RemediationRestClient restClient = new 
RemediationRestClient();
+
+    private final List<RemediationTO> lastRemediations = new ArrayList<>();
+
+    public RemediationsWidget(final String id, final PageReference pageRef) {
+        super(id);
+        setOutputMarkupId(true);
+
+        latestAlertsList.add(new 
IndicatorAjaxTimerBehavior(Duration.seconds(30)) {
+
+            private static final long serialVersionUID = 7298597675929755960L;
+
+            @Override
+            protected void onTimer(final AjaxRequestTarget target) {
+                if (!latestAlerts.getObject().equals(lastRemediations)) {
+                    refreshLatestAlerts(target);
+                }
+            }
+        });
+    }
+
+    public final void refreshLatestAlerts(final AjaxRequestTarget target) {
+        latestAlerts.getObject().clear();
+        latestAlerts.getObject().addAll(lastRemediations);
+
+        
linkAlertsNumber.setDefaultModelObject(latestAlerts.getObject().size());
+        target.add(linkAlertsNumber);
+
+        
headerAlertsNumber.setDefaultModelObject(latestAlerts.getObject().size());
+        target.add(headerAlertsNumber);
+
+        latestFive.removeAll();
+        target.add(latestAlertsList);
+
+        lastRemediations.clear();
+        lastRemediations.addAll(latestAlerts.getObject());
+    }
+
+    @Override
+    protected IModel<List<RemediationTO>> getLatestAlerts() {
+        return new ListModel<RemediationTO>() {
+
+            private static final long serialVersionUID = 541491929575585613L;
+
+            @Override
+            public List<RemediationTO> getObject() {
+                List<RemediationTO> updatedRemediations;
+                if 
(SyncopeConsoleSession.get().owns(StandardEntitlement.REMEDIATION_LIST)
+                        && 
SyncopeConsoleSession.get().owns(StandardEntitlement.REMEDIATION_READ)) {
+
+                    updatedRemediations = 
restClient.getRemediations().stream().
+                            
sorted(Comparator.comparing(RemediationTO::getInstant)).
+                            collect(Collectors.toList());
+                } else {
+                    updatedRemediations = 
Collections.<RemediationTO>emptyList();
+                }
+
+                return updatedRemediations;
+            }
+        };
+    }
+
+    @Override
+    protected Panel getAlertLink(final String panelid, final RemediationTO 
event) {
+        return new RemediationsWidget.InnerPanel(panelid, event);
+    }
+
+    @Override
+    protected AbstractLink getEventsLink(final String linkid) {
+        BookmarkablePageLink<Remediations> remediations = 
BookmarkablePageLinkBuilder.build(linkid, Remediations.class);
+        MetaDataRoleAuthorizationStrategy.authorize(remediations, 
WebPage.ENABLE, StandardEntitlement.REMEDIATION_LIST);
+        return remediations;
+    }
+
+    @Override
+    protected Icon getIcon(final String iconid) {
+        return new Icon(iconid,
+                
FontAwesomeIconTypeBuilder.on(FontAwesomeIconTypeBuilder.FontAwesomeGraphic.medkit).build());
+    }
+
+    public static final class InnerPanel extends Panel {
+
+        private static final long serialVersionUID = 8074027899915634928L;
+
+        public InnerPanel(final String id, final RemediationTO alert) {
+            super(id);
+
+            AjaxLink<String> approval = new AjaxLink<String>("remediation") {
+
+                private static final long serialVersionUID = 
7021195294339489084L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    // do nothing
+                }
+
+                @Override
+                protected void onComponentTag(final ComponentTag tag) {
+                    super.onComponentTag(tag);
+                    tag.put("title", alert.getRemoteName().trim());
+                }
+            };
+
+            add(approval);
+
+            approval.add(new Label("label", alert.getOperation().name() + " " 
+ alert.getAnyType()));
+
+            approval.add(new Label("resource", alert.getResource()));
+
+            approval.add(new Label("instant",
+                    
SyncopeConsoleSession.get().getDateFormat().format(alert.getInstant())).
+                    setRenderBodyOnly(true));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
index a7446fe..7455ffc 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrs.java
@@ -52,6 +52,8 @@ public abstract class AbstractAttrs<S extends SchemaTO> 
extends WizardStep imple
 
     private static final long serialVersionUID = -5387344116983102292L;
 
+    protected final Comparator<AttrTO> attrComparator = new AttrComparator();
+
     private final SchemaRestClient schemaRestClient = new SchemaRestClient();
 
     private final AnyTypeClassRestClient anyTypeClassRestClient = new 
AnyTypeClassRestClient();
@@ -208,7 +210,7 @@ public abstract class AbstractAttrs<S extends SchemaTO> 
extends WizardStep imple
         return null;
     }
 
-    protected class AttrComparator implements Comparator<AttrTO>, Serializable 
{
+    private class AttrComparator implements Comparator<AttrTO>, Serializable {
 
         private static final long serialVersionUID = -5105030477767941060L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/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..d52a2f7 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
@@ -42,7 +42,26 @@ public class AnyObjectWizardBuilder extends 
AnyWizardBuilder<AnyObjectTO> implem
             final AnyObjectFormLayoutInfo formLayoutInfo,
             final PageReference pageRef) {
 
-        super(anyObjectTO, anyTypeClasses, formLayoutInfo, pageRef);
+        super(anyObjectTO == null ? null : new AnyObjectWrapper(anyObjectTO), 
anyTypeClasses, formLayoutInfo, pageRef);
+    }
+
+    /**
+     * Constructor to be used for Remediation details only.
+     *
+     * @param previousAnyObjectTO previous anyObject status.
+     * @param anyObjectTO new anyObject status to be approved.
+     * @param anyTypeClasses any type classes.
+     * @param formLayoutInfo from layout.
+     * @param pageRef reference page.
+     */
+    public AnyObjectWizardBuilder(
+            final AnyObjectTO previousAnyObjectTO,
+            final AnyObjectTO anyObjectTO,
+            final List<String> anyTypeClasses,
+            final AnyObjectFormLayoutInfo formLayoutInfo,
+            final PageReference pageRef) {
+
+        super(new AnyObjectWrapper(previousAnyObjectTO, anyObjectTO), 
anyTypeClasses, formLayoutInfo, pageRef);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWrapper.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWrapper.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWrapper.java
new file mode 100644
index 0000000..3855bd8
--- /dev/null
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWrapper.java
@@ -0,0 +1,42 @@
+/*
+ * 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.wizards.any;
+
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+
+public class AnyObjectWrapper extends AnyWrapper<AnyObjectTO> {
+
+    private static final long serialVersionUID = 8058288034211558376L;
+
+    private AnyObjectTO previousAnyObjectTO;
+
+    public AnyObjectWrapper(final AnyObjectTO anyObjectTO) {
+        this(null, anyObjectTO);
+    }
+
+    public AnyObjectWrapper(final AnyObjectTO previousAnyObjectTO, final 
AnyObjectTO anyObjectTO) {
+        super(anyObjectTO);
+        this.previousAnyObjectTO = previousAnyObjectTO;
+    }
+
+    public AnyObjectTO getPreviousAnyObjectTO() {
+        return previousAnyObjectTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
index fe6444d..ccb378e 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.tabs.Accordion;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
@@ -98,16 +99,12 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
 
     @Override
     protected List<AttrTO> getAttrsFromTO() {
-        final List<AttrTO> res = new ArrayList<>(anyTO.getDerAttrs());
-        Collections.sort(res, new AttrComparator());
-        return res;
+        return 
anyTO.getDerAttrs().stream().sorted(attrComparator).collect(Collectors.toList());
     }
 
     @Override
     protected List<AttrTO> getAttrsFromTO(final MembershipTO membershipTO) {
-        final List<AttrTO> res = new ArrayList<>(membershipTO.getDerAttrs());
-        Collections.sort(res, new AttrComparator());
-        return res;
+        return 
membershipTO.getDerAttrs().stream().sorted(attrComparator).collect(Collectors.toList());
     }
 
     @Override
@@ -116,7 +113,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
 
         Map<String, AttrTO> attrMap = 
EntityTOUtils.buildAttrMap(anyTO.getDerAttrs());
 
-        for (DerSchemaTO schema : schemas.values()) {
+        schemas.values().forEach(schema -> {
             AttrTO attrTO = new AttrTO();
             attrTO.setSchema(schema.getKey());
             if (attrMap.containsKey(schema.getKey())) {
@@ -124,7 +121,7 @@ public class DerAttrs extends AbstractAttrs<DerSchemaTO> {
             }
 
             attrs.add(attrTO);
-        }
+        });
 
         anyTO.getDerAttrs().clear();
         anyTO.getDerAttrs().addAll(attrs);

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/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..4282fae 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
@@ -49,6 +49,25 @@ public class GroupWizardBuilder extends 
AnyWizardBuilder<GroupTO> implements Gro
     }
 
     /**
+     * Constructor to be used for Remediation details only.
+     *
+     * @param previousGroupTO previous group status.
+     * @param groupTO new group status to be approved.
+     * @param anyTypeClasses any type classes.
+     * @param formLayoutInfo from layout.
+     * @param pageRef reference page.
+     */
+    public GroupWizardBuilder(
+            final GroupTO previousGroupTO,
+            final GroupTO groupTO,
+            final List<String> anyTypeClasses,
+            final GroupFormLayoutInfo formLayoutInfo,
+            final PageReference pageRef) {
+
+        super(new GroupWrapper(previousGroupTO, groupTO), anyTypeClasses, 
formLayoutInfo, pageRef);
+    }
+
+    /**
      * This method has been overridden to manage asynchronous translation of 
FIQL string to search classes list and
      * viceversa.
      *

http://git-wip-us.apache.org/repos/asf/syncope/blob/4e781f4a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java
----------------------------------------------------------------------
diff --git 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java
 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java
index f64a345..8e82041 100644
--- 
a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java
+++ 
b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWrapper.java
@@ -31,16 +31,27 @@ public class GroupWrapper extends AnyWrapper<GroupTO> {
 
     private static final long serialVersionUID = 8058288034211558376L;
 
+    private GroupTO previousGroupTO;
+
     private List<SearchClause> uDynClauses;
 
     private Map<String, List<SearchClause>> aDynClauses;
 
     public GroupWrapper(final GroupTO groupTO) {
+        this(null, groupTO);
+    }
+
+    public GroupWrapper(final GroupTO previousGroupTO, final GroupTO groupTO) {
         super(groupTO);
+        this.previousGroupTO = previousGroupTO;
         getUDynClauses();
         getADynClauses();
     }
 
+    public GroupTO getPreviousGroupTO() {
+        return previousGroupTO;
+    }
+
     public final List<SearchClause> getUDynClauses() {
         if (this.uDynClauses == null) {
             this.uDynClauses = 
SearchUtils.getSearchClauses(this.anyTO.getUDynMembershipCond());

Reply via email to