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/31 17:28:58 UTC

[04/10] syncope git commit: [SYNCOPE-765] TODO -> Approval

[SYNCOPE-765] TODO -> Approval


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

Branch: refs/heads/master
Commit: ef80e01536b131ea818d84df9d8bd6b4a41824ed
Parents: e29d78c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Mar 31 14:09:50 2016 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Mar 31 14:09:50 2016 +0200

----------------------------------------------------------------------
 .../client/console/approvals/Approval.java      | 206 ++++++++++++++
 .../console/approvals/ApprovalDetails.java      |  48 ++++
 .../client/console/approvals/ApprovalModal.java |  83 ++++++
 .../approvals/ApprovalSearchResultPanel.java    | 215 ++++++++++++++
 .../syncope/client/console/pages/Approvals.java |  41 +++
 .../syncope/client/console/pages/BasePage.java  |   8 +-
 .../syncope/client/console/pages/TODOs.java     |  41 ---
 .../syncope/client/console/todos/Approval.java  | 206 --------------
 .../client/console/todos/ApprovalDetails.java   |  48 ----
 .../client/console/todos/ApprovalModal.java     |  83 ------
 .../console/todos/TODOSearchResultPanel.java    | 215 --------------
 .../client/console/widgets/ApprovalsWidget.java | 284 +++++++++++++++++++
 .../client/console/widgets/TODOsWidget.java     | 283 ------------------
 .../SyncopeConsoleApplication.properties        |   1 -
 .../SyncopeConsoleApplication_it.properties     |   1 -
 .../SyncopeConsoleApplication_pt_BR.properties  |   1 -
 .../client/console/approvals/Approval.html      |  34 +++
 .../console/approvals/Approval.properties       |  17 ++
 .../console/approvals/ApprovalDetails.html      |  26 ++
 .../client/console/approvals/ApprovalModal.html |  26 ++
 .../console/approvals/ApprovalModal.properties  |  17 ++
 .../console/approvals/Approval_it.properties    |  17 ++
 .../console/approvals/Approval_pt_BR.properties |  17 ++
 .../syncope/client/console/pages/Approvals.html |  35 +++
 .../client/console/pages/Approvals.properties   |  32 +++
 .../console/pages/Approvals_it.properties       |  32 +++
 .../console/pages/Approvals_pt_BR.properties    |  32 +++
 .../syncope/client/console/pages/BasePage.html  |   2 +-
 .../syncope/client/console/pages/TODOs.html     |  35 ---
 .../client/console/pages/TODOs.properties       |  32 ---
 .../client/console/pages/TODOs_it.properties    |  32 ---
 .../client/console/pages/TODOs_pt_BR.properties |  32 ---
 .../syncope/client/console/todos/Approval.html  |  34 ---
 .../client/console/todos/Approval.properties    |  17 --
 .../client/console/todos/ApprovalDetails.html   |  26 --
 .../client/console/todos/ApprovalModal.html     |  26 --
 .../console/todos/ApprovalModal.properties      |  17 --
 .../client/console/todos/Approval_it.properties |  17 --
 .../console/todos/Approval_pt_BR.properties     |  17 --
 .../client/console/widgets/ApprovalsWidget.html |  45 +++
 .../console/widgets/ApprovalsWidget.properties  |  21 ++
 .../widgets/ApprovalsWidget_it.properties       |  21 ++
 .../widgets/ApprovalsWidget_pt_BR.properties    |  21 ++
 .../client/console/widgets/TODOsWidget.html     |  48 ----
 .../console/widgets/TODOsWidget.properties      |  21 --
 .../console/widgets/TODOsWidget_it.properties   |  21 --
 .../widgets/TODOsWidget_pt_BR.properties        |  21 --
 .../syncope/fit/console/AnyObjectsITCase.java   |   7 +-
 .../syncope/fit/console/BulkActionITCase.java   |   1 -
 .../syncope/fit/console/GroupsITCase.java       |   5 +-
 .../apache/syncope/fit/console/LogsITCase.java  |   2 -
 .../syncope/fit/console/ParametersITCase.java   |   2 -
 .../apache/syncope/fit/console/RolesITCase.java |   2 -
 .../fit/console/SecurityQuestionsITCase.java    |   3 -
 .../apache/syncope/fit/console/UsersITCase.java |   5 +-
 55 files changed, 1282 insertions(+), 1300 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/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
new file mode 100644
index 0000000..b084d48
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/Approval.java
@@ -0,0 +1,206 @@
+/*
+ * 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.approvals;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.time.FastDateFormat;
+import org.apache.syncope.client.console.commons.MapChoiceRenderer;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSpinnerFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.DateTimeFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+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.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class Approval extends Panel {
+
+    private static final long serialVersionUID = -8847854414429745216L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Approval.class);
+
+    public Approval(final PageReference pageRef, final WorkflowFormTO formTO) {
+        super(MultilevelPanel.FIRST_LEVEL_ID);
+
+        IModel<List<WorkflowFormPropertyTO>> formProps = new LoadableDetachableModel<List<WorkflowFormPropertyTO>>() {
+
+            private static final long serialVersionUID = 3169142472626817508L;
+
+            @Override
+            protected List<WorkflowFormPropertyTO> load() {
+                return formTO.getProperties();
+            }
+        };
+
+        final ListView<WorkflowFormPropertyTO> propView = new ListView<WorkflowFormPropertyTO>("propView", formProps) {
+
+            private static final long serialVersionUID = 9101744072914090143L;
+
+            @Override
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            protected void populateItem(final ListItem<WorkflowFormPropertyTO> item) {
+                final WorkflowFormPropertyTO prop = item.getModelObject();
+
+                String label = StringUtils.isBlank(prop.getName()) ? prop.getId() : prop.getName();
+
+                FieldPanel field;
+                switch (prop.getType()) {
+                    case Boolean:
+                        field = new AjaxDropDownChoicePanel("value", label, new PropertyModel<String>(prop, "value") {
+
+                            private static final long serialVersionUID = -3743432456095828573L;
+
+                            @Override
+                            public String getObject() {
+                                return StringUtils.isBlank(prop.getValue())
+                                        ? null
+                                        : prop.getValue().equals("true") ? "Yes" : "No";
+                            }
+
+                            @Override
+                            public void setObject(final String object) {
+                                prop.setValue(String.valueOf(object.equalsIgnoreCase("yes")));
+                            }
+
+                        }, false).setChoices(Arrays.asList(new String[] { "Yes", "No" }));
+                        break;
+
+                    case Date:
+                        final FastDateFormat formatter = FastDateFormat.getInstance(prop.getDatePattern());
+                        field = new DateTimeFieldPanel("value", label, new PropertyModel<Date>(prop, "value") {
+
+                            private static final long serialVersionUID = -3743432456095828573L;
+
+                            @Override
+                            public Date getObject() {
+                                try {
+                                    if (StringUtils.isBlank(prop.getValue())) {
+                                        return null;
+                                    } else {
+                                        return formatter.parse(prop.getValue());
+                                    }
+                                } catch (ParseException e) {
+                                    LOG.error("Unparsable date: {}", prop.getValue(), e);
+                                    return null;
+                                }
+                            }
+
+                            @Override
+                            public void setObject(final Date object) {
+                                prop.setValue(formatter.format(object));
+                            }
+
+                        }, prop.getDatePattern());
+                        break;
+
+                    case Enum:
+                        MapChoiceRenderer<String, String> enumCR = new MapChoiceRenderer<>(prop.getEnumValues());
+                        final Map<String, String> map = MapUtils.invertMap(prop.getEnumValues());
+
+                        field = new AjaxDropDownChoicePanel(
+                                "value", label, new PropertyModel<String>(prop, "value"), false).
+                                setChoiceRenderer(enumCR).setChoices(new Model<ArrayList<String>>() {
+
+                            private static final long serialVersionUID = -858521070366432018L;
+
+                            @Override
+                            public ArrayList<String> getObject() {
+                                return new ArrayList<>(prop.getEnumValues().keySet());
+                            }
+                        });
+                        break;
+
+                    case Long:
+                        field = new AjaxSpinnerFieldPanel.Builder<Long>().build(
+                                "value",
+                                label,
+                                Long.class,
+                                new PropertyModel<Long>(prop, "value") {
+
+                            private static final long serialVersionUID = -7688359318035249200L;
+
+                            @Override
+                            public Long getObject() {
+                                return StringUtils.isBlank(prop.getValue())
+                                        ? null
+                                        : NumberUtils.toLong(prop.getValue());
+                            }
+
+                            @Override
+                            public void setObject(final Long object) {
+                                prop.setValue(String.valueOf(object));
+                            }
+                        });
+                        break;
+
+                    case String:
+                    default:
+                        field = new AjaxTextFieldPanel("value", label, new PropertyModel<String>(prop, "value"), false);
+                        break;
+                }
+
+                field.setReadOnly(!prop.isWritable());
+                if (prop.isRequired()) {
+                    field.addRequiredLabel();
+                }
+
+                item.add(field);
+            }
+        };
+
+        final AjaxLink<String> userDetails = new AjaxLink<String>("userDetails") {
+
+            private static final long serialVersionUID = -4804368561204623354L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                viewDetails(formTO, target);
+            }
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, StandardEntitlement.USER_READ);
+
+        add(propView);
+        add(userDetails);
+    }
+
+    protected abstract void viewDetails(final WorkflowFormTO formTO, final AjaxRequestTarget target);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
new file mode 100644
index 0000000..ec8cbcf
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalDetails.java
@@ -0,0 +1,48 @@
+/*
+ * 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.approvals;
+
+import java.util.List;
+import org.apache.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.UserRestClient;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.any.AnyHandler;
+import org.apache.syncope.client.console.wizards.any.UserWizardBuilder;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.PageReference;
+
+public class ApprovalDetails extends MultilevelPanel.SecondLevel {
+
+    private static final long serialVersionUID = -8847854414429745216L;
+
+    public ApprovalDetails(final PageReference pageRef, final WorkflowFormTO formTO) {
+        super(MultilevelPanel.SECOND_LEVEL_ID);
+
+        final UserTO userTO = new UserRestClient().read(formTO.getUserKey());
+        final List<String> anyTypeClasses = new AnyTypeRestClient().get(AnyTypeKind.USER.name()).getClasses();
+
+        final AjaxWizard<AnyHandler<UserTO>> wizard
+                = new UserWizardBuilder("wizard", userTO, anyTypeClasses, pageRef).build(AjaxWizard.Mode.READONLY);
+
+        add(wizard);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/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
new file mode 100644
index 0000000..f5367cf
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalModal.java
@@ -0,0 +1,83 @@
+/*
+ * 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.approvals;
+
+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.syncope.client.console.panels.MultilevelPanel;
+import org.apache.syncope.client.console.rest.UserWorkflowRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+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;
+
+public class ApprovalModal extends Panel implements ModalPanel<WorkflowFormTO> {
+
+    private static final long serialVersionUID = -8847854414429745216L;
+
+    private final BaseModal<?> modal;
+
+    private final WorkflowFormTO formTO;
+
+    public ApprovalModal(final BaseModal<?> modal, final PageReference pageRef, final WorkflowFormTO formTO) {
+        super(BaseModal.CONTENT_ID);
+        this.modal = modal;
+        this.formTO = formTO;
+
+        final MultilevelPanel mlp = new MultilevelPanel("approval");
+        add(mlp);
+
+        mlp.setFirstLevel(new Approval(pageRef, formTO) {
+
+            private static final long serialVersionUID = -2195387360323687302L;
+
+            @Override
+            protected void viewDetails(final WorkflowFormTO formTO, final AjaxRequestTarget target) {
+                mlp.next(getString("approval.details"), new ApprovalDetails(pageRef, formTO), target);
+            }
+        });
+    }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        try {
+            new UserWorkflowRestClient().submitForm(formTO);
+            this.modal.show(false);
+            this.modal.close(target);
+            SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
+        } catch (SyncopeClientException e) {
+            SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + e.getMessage());
+        }
+        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+    }
+
+    @Override
+    public void onError(final AjaxRequestTarget target, final Form<?> form) {
+        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+    }
+
+    @Override
+    public WorkflowFormTO getItem() {
+        return this.formTO;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalSearchResultPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalSearchResultPanel.java
new file mode 100644
index 0000000..2d97bf0
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/approvals/ApprovalSearchResultPanel.java
@@ -0,0 +1,215 @@
+/*
+ * 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.approvals;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+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.UserWorkflowRestClient;
+import org.apache.syncope.client.console.approvals.ApprovalSearchResultPanel.ApprovalProvider;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+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.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+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.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.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public class ApprovalSearchResultPanel
+        extends AbstractSearchResultPanel<WorkflowFormTO, WorkflowFormTO, ApprovalProvider, UserWorkflowRestClient> {
+
+    private static final long serialVersionUID = -7122136682275797903L;
+
+    public ApprovalSearchResultPanel(final String id, final PageReference pageReference) {
+        super(id, pageReference, false);
+        disableCheckBoxes();
+
+        setFooterVisibility(true);
+        modal.addSumbitButton();
+        modal.size(Modal.Size.Large);
+
+        restClient = new UserWorkflowRestClient();
+
+        initResultTable();
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, ENABLE, StandardEntitlement.WORKFLOW_FORM_SUBMIT);
+    }
+
+    @Override
+    protected List<IColumn<WorkflowFormTO, String>> getColumns() {
+
+        List<IColumn<WorkflowFormTO, String>> columns = new ArrayList<>();
+        columns.add(new PropertyColumn<WorkflowFormTO, String>(
+                new ResourceModel("taskId"), "taskId", "taskId"));
+        columns.add(new PropertyColumn<WorkflowFormTO, String>(
+                new ResourceModel("key"), "key", "key"));
+        columns.add(new PropertyColumn<WorkflowFormTO, String>(
+                new ResourceModel("description"), "description", "description"));
+        columns.add(new DatePropertyColumn<WorkflowFormTO>(
+                new ResourceModel("createTime"), "createTime", "createTime"));
+        columns.add(new DatePropertyColumn<WorkflowFormTO>(
+                new ResourceModel("dueDate"), "dueDate", "dueDate"));
+        columns.add(new PropertyColumn<WorkflowFormTO, String>(new ResourceModel("owner"), "owner", "owner"));
+        columns.add(new ActionColumn<WorkflowFormTO, String>(new ResourceModel("actions")) {
+
+            private static final long serialVersionUID = -3503023501954863133L;
+
+            @Override
+            public ActionLinksPanel<WorkflowFormTO> getActions(
+                    final String componentId, final IModel<WorkflowFormTO> model) {
+                final ActionLinksPanel.Builder<WorkflowFormTO> panel = ActionLinksPanel.builder();
+
+                panel.add(new ActionLink<WorkflowFormTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
+                        try {
+                            restClient.claimForm(model.getObject().getTaskId());
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+                        } catch (SyncopeClientException scee) {
+                            error(getString(Constants.ERROR) + ": " + scee.getMessage());
+                        }
+                        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
+                        target.add(container);
+                    }
+                }, ActionLink.ActionType.CLAIM, StandardEntitlement.WORKFLOW_FORM_CLAIM);
+
+                panel.add(new ActionLink<WorkflowFormTO>() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
+                        final IModel<WorkflowFormTO> formModel = new CompoundPropertyModel<>(model.getObject());
+                        modal.setFormModel(formModel);
+
+                        target.add(modal.setContent(new ApprovalModal(modal, pageRef, model.getObject())));
+                        modal.header(new Model<>(getString("approval.edit", new Model<>(model.getObject()))));
+
+                        modal.show(true);
+                    }
+
+                    @Override
+                    protected boolean statusCondition(final WorkflowFormTO modelObject) {
+                        return SyncopeConsoleSession.get().getSelfTO().getUsername().
+                                equals(model.getObject().getOwner());
+                    }
+
+                }, ActionLink.ActionType.EDIT, StandardEntitlement.WORKFLOW_FORM_READ);
+
+                return panel.build(componentId);
+            }
+
+            @Override
+            public ActionLinksPanel<WorkflowFormTO> getHeader(final String componentId) {
+                final ActionLinksPanel.Builder<WorkflowFormTO> panel = ActionLinksPanel.builder();
+
+                return panel.add(new ActionLink<WorkflowFormTO>() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
+                        if (target != null) {
+                            target.add(container);
+                        }
+                    }
+                }, ActionLink.ActionType.RELOAD, StandardEntitlement.WORKFLOW_FORM_LIST).build(componentId);
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    protected ApprovalProvider dataProvider() {
+        return new ApprovalProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_WORKFLOW_FORM_PAGINATOR_ROWS;
+    }
+
+    public static class ApprovalProvider extends SearchableDataProvider<WorkflowFormTO> {
+
+        private static final long serialVersionUID = -2311716167583335852L;
+
+        private final SortableDataProviderComparator<WorkflowFormTO> comparator;
+
+        private final UserWorkflowRestClient restClient = new UserWorkflowRestClient();
+
+        public ApprovalProvider(final int paginatorRows) {
+            super(paginatorRows);
+            setSort("createTime", SortOrder.DESCENDING);
+            this.comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<WorkflowFormTO> iterator(final long first, final long count) {
+            final List<WorkflowFormTO> list = restClient.getForms();
+            Collections.sort(list, comparator);
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getForms().size();
+        }
+
+        @Override
+        public IModel<WorkflowFormTO> model(final WorkflowFormTO configuration) {
+            return new AbstractReadOnlyModel<WorkflowFormTO>() {
+
+                private static final long serialVersionUID = -2566070996511906708L;
+
+                @Override
+                public WorkflowFormTO getObject() {
+                    return configuration;
+                }
+            };
+        }
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBulkActions() {
+        return Collections.<ActionLink.ActionType>emptyList();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
new file mode 100644
index 0000000..a93d7c3
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Approvals.java
@@ -0,0 +1,41 @@
+/*
+ * 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.approvals.ApprovalSearchResultPanel;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.request.mapper.parameter.PageParameters;
+
+public class Approvals extends BasePage {
+
+    private static final long serialVersionUID = -1100228004207271271L;
+
+    public Approvals(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 ApprovalSearchResultPanel("wfPanel", getPageReference()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/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 44fc786..334b095 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
@@ -30,7 +30,7 @@ import org.apache.syncope.client.console.panels.NotificationPanel;
 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.TODOsWidget;
+import org.apache.syncope.client.console.widgets.ApprovalsWidget;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
@@ -96,7 +96,7 @@ public class BasePage extends WebPage implements IAjaxIndicatorAware {
         body.add(new Label("version", SyncopeConsoleApplication.get().getVersion()));
         body.add(new Label("username", SyncopeConsoleSession.get().getSelfTO().getUsername()));
 
-        body.add(new TODOsWidget("todosWidget", getPageReference()).setRenderBodyOnly(true));
+        body.add(new ApprovalsWidget("approvalsWidget", getPageReference()).setRenderBodyOnly(true));
 
         // menu
         WebMarkupContainer liContainer = new WebMarkupContainer(getLIContainerId("dashboard"));
@@ -239,8 +239,8 @@ public class BasePage extends WebPage implements IAjaxIndicatorAware {
         }
 
         // Extensions
-        ClassPathScanImplementationLookup classPathScanImplementationLookup
-                = (ClassPathScanImplementationLookup) SyncopeConsoleApplication.get().
+        ClassPathScanImplementationLookup classPathScanImplementationLookup =
+                (ClassPathScanImplementationLookup) SyncopeConsoleApplication.get().
                 getServletContext().getAttribute(ConsoleInitializer.CLASSPATH_LOOKUP);
         List<Class<? extends BaseExtPage>> extPageClasses = classPathScanImplementationLookup.getExtPageClasses();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/pages/TODOs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/TODOs.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/TODOs.java
deleted file mode 100644
index ac51ba0..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/TODOs.java
+++ /dev/null
@@ -1,41 +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.pages;
-
-import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
-import org.apache.syncope.client.console.todos.TODOSearchResultPanel;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.request.mapper.parameter.PageParameters;
-
-public class TODOs extends BasePage {
-
-    private static final long serialVersionUID = -1100228004207271271L;
-
-    public TODOs(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 TODOSearchResultPanel("wfPanel", getPageReference()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/todos/Approval.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/todos/Approval.java b/client/console/src/main/java/org/apache/syncope/client/console/todos/Approval.java
deleted file mode 100644
index 9e5e3ac..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/todos/Approval.java
+++ /dev/null
@@ -1,206 +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.todos;
-
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import org.apache.commons.collections4.MapUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.math.NumberUtils;
-import org.apache.commons.lang3.time.FastDateFormat;
-import org.apache.syncope.client.console.commons.MapChoiceRenderer;
-import org.apache.syncope.client.console.panels.MultilevelPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxSpinnerFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.DateTimeFieldPanel;
-import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
-import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
-import org.apache.syncope.common.lib.to.WorkflowFormTO;
-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.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.LoadableDetachableModel;
-import org.apache.wicket.model.Model;
-import org.apache.wicket.model.PropertyModel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class Approval extends Panel {
-
-    private static final long serialVersionUID = -8847854414429745216L;
-
-    protected static final Logger LOG = LoggerFactory.getLogger(Approval.class);
-
-    public Approval(final PageReference pageRef, final WorkflowFormTO formTO) {
-        super(MultilevelPanel.FIRST_LEVEL_ID);
-
-        IModel<List<WorkflowFormPropertyTO>> formProps = new LoadableDetachableModel<List<WorkflowFormPropertyTO>>() {
-
-            private static final long serialVersionUID = 3169142472626817508L;
-
-            @Override
-            protected List<WorkflowFormPropertyTO> load() {
-                return formTO.getProperties();
-            }
-        };
-
-        final ListView<WorkflowFormPropertyTO> propView = new ListView<WorkflowFormPropertyTO>("propView", formProps) {
-
-            private static final long serialVersionUID = 9101744072914090143L;
-
-            @Override
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            protected void populateItem(final ListItem<WorkflowFormPropertyTO> item) {
-                final WorkflowFormPropertyTO prop = item.getModelObject();
-
-                String label = StringUtils.isBlank(prop.getName()) ? prop.getId() : prop.getName();
-
-                FieldPanel field;
-                switch (prop.getType()) {
-                    case Boolean:
-                        field = new AjaxDropDownChoicePanel("value", label, new PropertyModel<String>(prop, "value") {
-
-                            private static final long serialVersionUID = -3743432456095828573L;
-
-                            @Override
-                            public String getObject() {
-                                return StringUtils.isBlank(prop.getValue())
-                                        ? null
-                                        : prop.getValue().equals("true") ? "Yes" : "No";
-                            }
-
-                            @Override
-                            public void setObject(final String object) {
-                                prop.setValue(String.valueOf(object.equalsIgnoreCase("yes")));
-                            }
-
-                        }, false).setChoices(Arrays.asList(new String[] { "Yes", "No" }));
-                        break;
-
-                    case Date:
-                        final FastDateFormat formatter = FastDateFormat.getInstance(prop.getDatePattern());
-                        field = new DateTimeFieldPanel("value", label, new PropertyModel<Date>(prop, "value") {
-
-                            private static final long serialVersionUID = -3743432456095828573L;
-
-                            @Override
-                            public Date getObject() {
-                                try {
-                                    if (StringUtils.isBlank(prop.getValue())) {
-                                        return null;
-                                    } else {
-                                        return formatter.parse(prop.getValue());
-                                    }
-                                } catch (ParseException e) {
-                                    LOG.error("Unparsable date: {}", prop.getValue(), e);
-                                    return null;
-                                }
-                            }
-
-                            @Override
-                            public void setObject(final Date object) {
-                                prop.setValue(formatter.format(object));
-                            }
-
-                        }, prop.getDatePattern());
-                        break;
-
-                    case Enum:
-                        MapChoiceRenderer<String, String> enumCR = new MapChoiceRenderer<>(prop.getEnumValues());
-                        final Map<String, String> map = MapUtils.invertMap(prop.getEnumValues());
-
-                        field = new AjaxDropDownChoicePanel(
-                                "value", label, new PropertyModel<String>(prop, "value"), false).
-                                setChoiceRenderer(enumCR).setChoices(new Model<ArrayList<String>>() {
-
-                            private static final long serialVersionUID = -858521070366432018L;
-
-                            @Override
-                            public ArrayList<String> getObject() {
-                                return new ArrayList<>(prop.getEnumValues().keySet());
-                            }
-                        });
-                        break;
-
-                    case Long:
-                        field = new AjaxSpinnerFieldPanel.Builder<Long>().build(
-                                "value",
-                                label,
-                                Long.class,
-                                new PropertyModel<Long>(prop, "value") {
-
-                            private static final long serialVersionUID = -7688359318035249200L;
-
-                            @Override
-                            public Long getObject() {
-                                return StringUtils.isBlank(prop.getValue())
-                                        ? null
-                                        : NumberUtils.toLong(prop.getValue());
-                            }
-
-                            @Override
-                            public void setObject(final Long object) {
-                                prop.setValue(String.valueOf(object));
-                            }
-                        });
-                        break;
-
-                    case String:
-                    default:
-                        field = new AjaxTextFieldPanel("value", label, new PropertyModel<String>(prop, "value"), false);
-                        break;
-                }
-
-                field.setReadOnly(!prop.isWritable());
-                if (prop.isRequired()) {
-                    field.addRequiredLabel();
-                }
-
-                item.add(field);
-            }
-        };
-
-        final AjaxLink<String> userDetails = new AjaxLink<String>("userDetails") {
-
-            private static final long serialVersionUID = -4804368561204623354L;
-
-            @Override
-            public void onClick(final AjaxRequestTarget target) {
-                viewDetails(formTO, target);
-            }
-        };
-        MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, StandardEntitlement.USER_READ);
-
-        add(propView);
-        add(userDetails);
-    }
-
-    protected abstract void viewDetails(final WorkflowFormTO formTO, final AjaxRequestTarget target);
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalDetails.java
deleted file mode 100644
index 20613d9..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalDetails.java
+++ /dev/null
@@ -1,48 +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.todos;
-
-import java.util.List;
-import org.apache.syncope.client.console.panels.MultilevelPanel;
-import org.apache.syncope.client.console.rest.AnyTypeRestClient;
-import org.apache.syncope.client.console.rest.UserRestClient;
-import org.apache.syncope.client.console.wizards.AjaxWizard;
-import org.apache.syncope.client.console.wizards.any.AnyHandler;
-import org.apache.syncope.client.console.wizards.any.UserWizardBuilder;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.to.WorkflowFormTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.wicket.PageReference;
-
-public class ApprovalDetails extends MultilevelPanel.SecondLevel {
-
-    private static final long serialVersionUID = -8847854414429745216L;
-
-    public ApprovalDetails(final PageReference pageRef, final WorkflowFormTO formTO) {
-        super(MultilevelPanel.SECOND_LEVEL_ID);
-
-        final UserTO userTO = new UserRestClient().read(formTO.getUserKey());
-        final List<String> anyTypeClasses = new AnyTypeRestClient().get(AnyTypeKind.USER.name()).getClasses();
-
-        final AjaxWizard<AnyHandler<UserTO>> wizard
-                = new UserWizardBuilder("wizard", userTO, anyTypeClasses, pageRef).build(AjaxWizard.Mode.READONLY);
-
-        add(wizard);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalModal.java b/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalModal.java
deleted file mode 100644
index a4fa1b5..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/todos/ApprovalModal.java
+++ /dev/null
@@ -1,83 +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.todos;
-
-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.syncope.client.console.panels.MultilevelPanel;
-import org.apache.syncope.client.console.rest.UserWorkflowRestClient;
-import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.WorkflowFormTO;
-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;
-
-public class ApprovalModal extends Panel implements ModalPanel<WorkflowFormTO> {
-
-    private static final long serialVersionUID = -8847854414429745216L;
-
-    private final BaseModal<?> modal;
-
-    private final WorkflowFormTO formTO;
-
-    public ApprovalModal(final BaseModal<?> modal, final PageReference pageRef, final WorkflowFormTO formTO) {
-        super(BaseModal.CONTENT_ID);
-        this.modal = modal;
-        this.formTO = formTO;
-
-        final MultilevelPanel mlp = new MultilevelPanel("approval");
-        add(mlp);
-
-        mlp.setFirstLevel(new Approval(pageRef, formTO) {
-
-            private static final long serialVersionUID = -2195387360323687302L;
-
-            @Override
-            protected void viewDetails(final WorkflowFormTO formTO, final AjaxRequestTarget target) {
-                mlp.next(getString("approval.details"), new ApprovalDetails(pageRef, formTO), target);
-            }
-        });
-    }
-
-    @Override
-    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
-        try {
-            new UserWorkflowRestClient().submitForm(formTO);
-            this.modal.show(false);
-            this.modal.close(target);
-            SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
-        } catch (SyncopeClientException e) {
-            SyncopeConsoleSession.get().error(getString(Constants.ERROR) + ": " + e.getMessage());
-        }
-        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
-    }
-
-    @Override
-    public void onError(final AjaxRequestTarget target, final Form<?> form) {
-        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
-    }
-
-    @Override
-    public WorkflowFormTO getItem() {
-        return this.formTO;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/todos/TODOSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/todos/TODOSearchResultPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/todos/TODOSearchResultPanel.java
deleted file mode 100644
index ff789b4..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/todos/TODOSearchResultPanel.java
+++ /dev/null
@@ -1,215 +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.todos;
-
-import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-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.UserWorkflowRestClient;
-import org.apache.syncope.client.console.todos.TODOSearchResultPanel.ApprovalProvider;
-import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
-import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
-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.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.WorkflowFormTO;
-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.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.Model;
-import org.apache.wicket.model.ResourceModel;
-
-public class TODOSearchResultPanel
-        extends AbstractSearchResultPanel<WorkflowFormTO, WorkflowFormTO, ApprovalProvider, UserWorkflowRestClient> {
-
-    private static final long serialVersionUID = -7122136682275797903L;
-
-    public TODOSearchResultPanel(final String id, final PageReference pageReference) {
-        super(id, pageReference, false);
-        disableCheckBoxes();
-
-        setFooterVisibility(true);
-        modal.addSumbitButton();
-        modal.size(Modal.Size.Large);
-
-        restClient = new UserWorkflowRestClient();
-
-        initResultTable();
-
-        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, ENABLE, StandardEntitlement.WORKFLOW_FORM_SUBMIT);
-    }
-
-    @Override
-    protected List<IColumn<WorkflowFormTO, String>> getColumns() {
-
-        List<IColumn<WorkflowFormTO, String>> columns = new ArrayList<>();
-        columns.add(new PropertyColumn<WorkflowFormTO, String>(
-                new ResourceModel("taskId"), "taskId", "taskId"));
-        columns.add(new PropertyColumn<WorkflowFormTO, String>(
-                new ResourceModel("key"), "key", "key"));
-        columns.add(new PropertyColumn<WorkflowFormTO, String>(
-                new ResourceModel("description"), "description", "description"));
-        columns.add(new DatePropertyColumn<WorkflowFormTO>(
-                new ResourceModel("createTime"), "createTime", "createTime"));
-        columns.add(new DatePropertyColumn<WorkflowFormTO>(
-                new ResourceModel("dueDate"), "dueDate", "dueDate"));
-        columns.add(new PropertyColumn<WorkflowFormTO, String>(new ResourceModel("owner"), "owner", "owner"));
-        columns.add(new ActionColumn<WorkflowFormTO, String>(new ResourceModel("actions")) {
-
-            private static final long serialVersionUID = -3503023501954863133L;
-
-            @Override
-            public ActionLinksPanel<WorkflowFormTO> getActions(
-                    final String componentId, final IModel<WorkflowFormTO> model) {
-                final ActionLinksPanel.Builder<WorkflowFormTO> panel = ActionLinksPanel.builder();
-
-                panel.add(new ActionLink<WorkflowFormTO>() {
-
-                    private static final long serialVersionUID = -3722207913631435501L;
-
-                    @Override
-                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
-                        try {
-                            restClient.claimForm(model.getObject().getTaskId());
-                            info(getString(Constants.OPERATION_SUCCEEDED));
-                        } catch (SyncopeClientException scee) {
-                            error(getString(Constants.ERROR) + ": " + scee.getMessage());
-                        }
-                        SyncopeConsoleSession.get().getNotificationPanel().refresh(target);
-                        target.add(container);
-                    }
-                }, ActionLink.ActionType.CLAIM, StandardEntitlement.WORKFLOW_FORM_CLAIM);
-
-                panel.add(new ActionLink<WorkflowFormTO>() {
-
-                    private static final long serialVersionUID = -3722207913631435501L;
-
-                    @Override
-                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
-                        final IModel<WorkflowFormTO> formModel = new CompoundPropertyModel<>(model.getObject());
-                        modal.setFormModel(formModel);
-
-                        target.add(modal.setContent(new ApprovalModal(modal, pageRef, model.getObject())));
-                        modal.header(new Model<>(getString("approval.edit", new Model<>(model.getObject()))));
-
-                        modal.show(true);
-                    }
-
-                    @Override
-                    protected boolean statusCondition(final WorkflowFormTO modelObject) {
-                        return SyncopeConsoleSession.get().getSelfTO().getUsername().
-                                equals(model.getObject().getOwner());
-                    }
-
-                }, ActionLink.ActionType.EDIT, StandardEntitlement.WORKFLOW_FORM_READ);
-
-                return panel.build(componentId);
-            }
-
-            @Override
-            public ActionLinksPanel<WorkflowFormTO> getHeader(final String componentId) {
-                final ActionLinksPanel.Builder<WorkflowFormTO> panel = ActionLinksPanel.builder();
-
-                return panel.add(new ActionLink<WorkflowFormTO>() {
-
-                    private static final long serialVersionUID = -7978723352517770644L;
-
-                    @Override
-                    public void onClick(final AjaxRequestTarget target, final WorkflowFormTO ignore) {
-                        if (target != null) {
-                            target.add(container);
-                        }
-                    }
-                }, ActionLink.ActionType.RELOAD, StandardEntitlement.WORKFLOW_FORM_LIST).build(componentId);
-            }
-        });
-
-        return columns;
-    }
-
-    @Override
-    protected ApprovalProvider dataProvider() {
-        return new ApprovalProvider(rows);
-    }
-
-    @Override
-    protected String paginatorRowsKey() {
-        return Constants.PREF_WORKFLOW_FORM_PAGINATOR_ROWS;
-    }
-
-    public static class ApprovalProvider extends SearchableDataProvider<WorkflowFormTO> {
-
-        private static final long serialVersionUID = -2311716167583335852L;
-
-        private final SortableDataProviderComparator<WorkflowFormTO> comparator;
-
-        private final UserWorkflowRestClient restClient = new UserWorkflowRestClient();
-
-        public ApprovalProvider(final int paginatorRows) {
-            super(paginatorRows);
-            setSort("createTime", SortOrder.DESCENDING);
-            this.comparator = new SortableDataProviderComparator<>(this);
-        }
-
-        @Override
-        public Iterator<WorkflowFormTO> iterator(final long first, final long count) {
-            final List<WorkflowFormTO> list = restClient.getForms();
-            Collections.sort(list, comparator);
-            return list.subList((int) first, (int) first + (int) count).iterator();
-        }
-
-        @Override
-        public long size() {
-            return restClient.getForms().size();
-        }
-
-        @Override
-        public IModel<WorkflowFormTO> model(final WorkflowFormTO configuration) {
-            return new AbstractReadOnlyModel<WorkflowFormTO>() {
-
-                private static final long serialVersionUID = -2566070996511906708L;
-
-                @Override
-                public WorkflowFormTO getObject() {
-                    return configuration;
-                }
-            };
-        }
-    }
-
-    @Override
-    protected Collection<ActionLink.ActionType> getBulkActions() {
-        return Collections.<ActionLink.ActionType>emptyList();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ef80e015/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
new file mode 100644
index 0000000..fd19e97
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/ApprovalsWidget.java
@@ -0,0 +1,284 @@
+/*
+ * 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.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.Approvals;
+import org.apache.syncope.client.console.rest.UserWorkflowRestClient;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.Application;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ThreadContext;
+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.event.IEvent;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.LoadableDetachableModel;
+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 ApprovalsWidget extends Panel {
+
+    private static final long serialVersionUID = 7667120094526529934L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(ApprovalsWidget.class);
+
+    private static final int UPDATE_PERIOD = 30;
+
+    private final Label linkApprovalsNumber;
+
+    private final Label headerApprovalsNumber;
+
+    private final WebMarkupContainer lastApprovalsList;
+
+    private final ListView<WorkflowFormTO> lastFive;
+
+    private final List<WorkflowFormTO> lastApprovals;
+
+    public ApprovalsWidget(final String id, final PageReference pageRef) {
+        super(id);
+        setOutputMarkupId(true);
+
+        LoadableDetachableModel<List<WorkflowFormTO>> model = new LoadableDetachableModel<List<WorkflowFormTO>>() {
+
+            private static final long serialVersionUID = 7474274077691068779L;
+
+            @Override
+            protected List<WorkflowFormTO> load() {
+                return ApprovalsWidget.this.lastApprovals.subList(0, ApprovalsWidget.this.lastApprovals.size() < 6
+                        ? ApprovalsWidget.this.lastApprovals.size() : 5);
+            }
+        };
+
+        lastApprovals = getLastApprovals();
+        Collections.sort(lastApprovals, new WorkflowFormComparator());
+
+        linkApprovalsNumber = new Label("approvals", lastApprovals.size()) {
+
+            private static final long serialVersionUID = 4755868673082976208L;
+
+            @Override
+            protected void onComponentTag(final ComponentTag tag) {
+                super.onComponentTag(tag);
+                if (Integer.valueOf(getDefaultModelObject().toString()) > 0) {
+                    tag.put("class", "label label-danger");
+                } else {
+                    tag.put("class", "label label-info");
+                }
+            }
+
+        };
+        add(linkApprovalsNumber.setOutputMarkupId(true));
+
+        headerApprovalsNumber = new Label("number", lastApprovals.size());
+        headerApprovalsNumber.setOutputMarkupId(true);
+        add(headerApprovalsNumber);
+
+        lastApprovalsList = new WebMarkupContainer("lastApprovalsList");
+        lastApprovalsList.setOutputMarkupId(true);
+        add(lastApprovalsList);
+
+        lastFive = new ListView<WorkflowFormTO>("lastApprovals", model) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem<WorkflowFormTO> item) {
+                final WorkflowFormTO modelObject = item.getModelObject();
+
+                final AjaxLink<String> approval = new AjaxLink<String>("approval") {
+
+                    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);
+                        if (StringUtils.isNotBlank(modelObject.getDescription())) {
+                            tag.put("title", modelObject.getDescription().trim());
+                        }
+                    }
+                };
+
+                item.add(approval);
+
+                approval.add(new Label("key", new ResourceModel(modelObject.getKey(), modelObject.getKey())).
+                        setRenderBodyOnly(true));
+
+                approval.add(new Label("owner", modelObject.getOwner()));
+
+                approval.add(new Label("createTime",
+                        SyncopeConsoleSession.get().getDateFormat().format(modelObject.getCreateTime())).
+                        setRenderBodyOnly(true));
+
+                WebMarkupContainer dueDateContainer = new WebMarkupContainer("dueDateContainer");
+                dueDateContainer.setOutputMarkupId(true);
+                approval.add(dueDateContainer);
+
+                if (modelObject.getDueDate() == null) {
+                    dueDateContainer.add(new Label("dueDate"));
+                    dueDateContainer.setVisible(false);
+                } else {
+                    dueDateContainer.add(new Label("dueDate",
+                            SyncopeConsoleSession.get().getDateFormat().format(modelObject.getDueDate())).
+                            setRenderBodyOnly(true));
+                }
+
+            }
+        };
+        lastApprovalsList.add(lastFive.setReuseItems(false).setOutputMarkupId(true));
+
+        BookmarkablePageLink<Object> approvals = BookmarkablePageLinkBuilder.build("approvalsLink", Approvals.class);
+        add(approvals);
+        MetaDataRoleAuthorizationStrategy.authorize(approvals, WebPage.ENABLE, StandardEntitlement.WORKFLOW_FORM_LIST);
+
+        add(new WebSocketBehavior() {
+
+            private static final long serialVersionUID = 7944352891541344021L;
+
+            @Override
+            protected void onConnect(final ConnectedMessage message) {
+                super.onConnect(message);
+                SyncopeConsoleSession.get().scheduleAtFixedRate(
+                        new ApprovalInfoUpdater(message), 0, UPDATE_PERIOD, TimeUnit.SECONDS);
+            }
+        });
+    }
+
+    private List<WorkflowFormTO> getLastApprovals() {
+        if (SyncopeConsoleSession.get().owns(StandardEntitlement.WORKFLOW_FORM_LIST)) {
+            return new UserWorkflowRestClient().getForms();
+        } else {
+            return Collections.<WorkflowFormTO>emptyList();
+        }
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof WebSocketPushPayload) {
+            WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload();
+            if (wsEvent.getMessage() instanceof UpdateMessage) {
+
+                ApprovalsWidget.this.linkApprovalsNumber.
+                        setDefaultModelObject(ApprovalsWidget.this.lastApprovals.size());
+                wsEvent.getHandler().add(ApprovalsWidget.this.linkApprovalsNumber);
+
+                ApprovalsWidget.this.headerApprovalsNumber.
+                        setDefaultModelObject(ApprovalsWidget.this.lastApprovals.size());
+                wsEvent.getHandler().add(ApprovalsWidget.this.headerApprovalsNumber);
+
+                ApprovalsWidget.this.lastFive.removeAll();
+                wsEvent.getHandler().add(ApprovalsWidget.this.lastApprovalsList);
+            }
+        } else {
+            super.onEvent(event);
+        }
+    }
+
+    protected final class ApprovalInfoUpdater implements Runnable {
+
+        private final Application application;
+
+        private final SyncopeConsoleSession session;
+
+        private final IKey key;
+
+        public ApprovalInfoUpdater(final ConnectedMessage message) {
+            this.application = message.getApplication();
+            this.session = SyncopeConsoleSession.get();
+            this.key = message.getKey();
+        }
+
+        @Override
+        public void run() {
+            try {
+                ThreadContext.setApplication(application);
+                ThreadContext.setSession(session);
+
+                final List<WorkflowFormTO> actual = getLastApprovals();
+                Collections.sort(actual, new WorkflowFormComparator());
+
+                if (!actual.equals(lastApprovals)) {
+                    LOG.debug("Update approvals");
+
+                    lastApprovals.clear();
+                    lastApprovals.addAll(actual);
+
+                    WebSocketSettings settings = WebSocketSettings.Holder.get(application);
+                    WebSocketPushBroadcaster broadcaster =
+                            new WebSocketPushBroadcaster(settings.getConnectionRegistry());
+                    broadcaster.broadcast(new ConnectedMessage(application, session.getId(), key), new UpdateMessage());
+                }
+            } catch (Throwable t) {
+                LOG.error("Unexpected error while checking for updated approval info", t);
+            } finally {
+                ThreadContext.detach();
+            }
+        }
+    }
+
+    private static class UpdateMessage implements IWebSocketPushMessage, Serializable {
+
+        private static final long serialVersionUID = -824793424112532838L;
+
+    }
+
+    private class WorkflowFormComparator implements Comparator<WorkflowFormTO> {
+
+        @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/ef80e015/client/console/src/main/java/org/apache/syncope/client/console/widgets/TODOsWidget.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/TODOsWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/TODOsWidget.java
deleted file mode 100644
index 8b50826..0000000
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/TODOsWidget.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.console.widgets;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
-import org.apache.syncope.client.console.pages.TODOs;
-import org.apache.syncope.client.console.rest.UserWorkflowRestClient;
-import org.apache.syncope.common.lib.to.WorkflowFormTO;
-import org.apache.syncope.common.lib.types.StandardEntitlement;
-import org.apache.wicket.Application;
-import org.apache.wicket.PageReference;
-import org.apache.wicket.ThreadContext;
-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.event.IEvent;
-import org.apache.wicket.markup.ComponentTag;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.link.BookmarkablePageLink;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.list.ListView;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.LoadableDetachableModel;
-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 TODOsWidget extends Panel {
-
-    private static final long serialVersionUID = 7667120094526529934L;
-
-    private static final Logger LOG = LoggerFactory.getLogger(TODOsWidget.class);
-
-    private static final int UPDATE_PERIOD = 30;
-
-    private final Label linkTODOsNumber;
-
-    private final Label headerTODOsNumber;
-
-    private final WebMarkupContainer lastTODOsList;
-
-    private final ListView<WorkflowFormTO> lastFive;
-
-    private List<WorkflowFormTO> lastTODOs;
-
-    public TODOsWidget(final String id, final PageReference pageRef) {
-        super(id);
-        setOutputMarkupId(true);
-
-        LoadableDetachableModel<List<WorkflowFormTO>> model = new LoadableDetachableModel<List<WorkflowFormTO>>() {
-
-            private static final long serialVersionUID = 7474274077691068779L;
-
-            @Override
-            protected List<WorkflowFormTO> load() {
-                return TODOsWidget.this.lastTODOs.subList(
-                        0, TODOsWidget.this.lastTODOs.size() < 6 ? TODOsWidget.this.lastTODOs.size() : 5);
-            }
-        };
-
-        lastTODOs = getLastTODOs();
-        Collections.sort(lastTODOs, new WorkflowFormComparator());
-
-        linkTODOsNumber = new Label("todos", lastTODOs.size()) {
-
-            private static final long serialVersionUID = 4755868673082976208L;
-
-            @Override
-            protected void onComponentTag(final ComponentTag tag) {
-                super.onComponentTag(tag);
-                if (Integer.valueOf(getDefaultModelObject().toString()) > 0) {
-                    tag.put("class", "label label-danger");
-                } else {
-                    tag.put("class", "label label-info");
-                }
-            }
-
-        };
-        add(linkTODOsNumber.setOutputMarkupId(true));
-
-        headerTODOsNumber = new Label("number", lastTODOs.size());
-        headerTODOsNumber.setOutputMarkupId(true);
-        add(headerTODOsNumber);
-
-        lastTODOsList = new WebMarkupContainer("lastTODOsList");
-        lastTODOsList.setOutputMarkupId(true);
-        add(lastTODOsList);
-
-        lastFive = new ListView<WorkflowFormTO>("lastTODOs", model) {
-
-            private static final long serialVersionUID = 4949588177564901031L;
-
-            @Override
-            protected void populateItem(final ListItem<WorkflowFormTO> item) {
-                final WorkflowFormTO modelObject = item.getModelObject();
-
-                final AjaxLink<String> todo = new AjaxLink<String>("todo") {
-
-                    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);
-                        if (StringUtils.isNotBlank(modelObject.getDescription())) {
-                            tag.put("title", modelObject.getDescription().trim());
-                        }
-                    }
-                };
-
-                item.add(todo);
-
-                todo.add(new Label("key", new ResourceModel(modelObject.getKey(), modelObject.getKey())).
-                        setRenderBodyOnly(true));
-
-                todo.add(new Label("owner", modelObject.getOwner()));
-
-                todo.add(new Label("createTime",
-                        SyncopeConsoleSession.get().getDateFormat().format(modelObject.getCreateTime())).
-                        setRenderBodyOnly(true));
-
-                WebMarkupContainer dueDateContainer = new WebMarkupContainer("dueDateContainer");
-                dueDateContainer.setOutputMarkupId(true);
-                todo.add(dueDateContainer);
-
-                if (modelObject.getDueDate() == null) {
-                    dueDateContainer.add(new Label("dueDate"));
-                    dueDateContainer.setVisible(false);
-                } else {
-                    dueDateContainer.add(new Label("dueDate",
-                            SyncopeConsoleSession.get().getDateFormat().format(modelObject.getDueDate())).
-                            setRenderBodyOnly(true));
-                }
-
-            }
-        };
-        lastTODOsList.add(lastFive.setReuseItems(false).setOutputMarkupId(true));
-
-        final BookmarkablePageLink<Object> todos = BookmarkablePageLinkBuilder.build("todosLink", TODOs.class);
-        add(todos);
-
-        MetaDataRoleAuthorizationStrategy.authorize(todos, WebPage.ENABLE, StandardEntitlement.WORKFLOW_FORM_LIST);
-
-        add(new WebSocketBehavior() {
-
-            private static final long serialVersionUID = 7944352891541344021L;
-
-            @Override
-            protected void onConnect(final ConnectedMessage message) {
-                super.onConnect(message);
-                SyncopeConsoleSession.get().scheduleAtFixedRate(
-                        new TODOInfoUpdater(message), 0, UPDATE_PERIOD, TimeUnit.SECONDS);
-            }
-        });
-    }
-
-    private List<WorkflowFormTO> getLastTODOs() {
-        if (SyncopeConsoleSession.get().owns(StandardEntitlement.WORKFLOW_FORM_LIST)) {
-            return new UserWorkflowRestClient().getForms();
-        } else {
-            return Collections.<WorkflowFormTO>emptyList();
-        }
-    }
-
-    @Override
-    public void onEvent(final IEvent<?> event) {
-        if (event.getPayload() instanceof WebSocketPushPayload) {
-            WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload();
-            if (wsEvent.getMessage() instanceof UpdateMessage) {
-
-                TODOsWidget.this.linkTODOsNumber.setDefaultModelObject(TODOsWidget.this.lastTODOs.size());
-                wsEvent.getHandler().add(TODOsWidget.this.linkTODOsNumber);
-
-                TODOsWidget.this.headerTODOsNumber.setDefaultModelObject(TODOsWidget.this.lastTODOs.size());
-                wsEvent.getHandler().add(TODOsWidget.this.headerTODOsNumber);
-
-                TODOsWidget.this.lastFive.removeAll();
-                wsEvent.getHandler().add(TODOsWidget.this.lastTODOsList);
-            }
-        } else {
-            super.onEvent(event);
-        }
-    }
-
-    protected final class TODOInfoUpdater implements Runnable {
-
-        private final Application application;
-
-        private final SyncopeConsoleSession session;
-
-        private final IKey key;
-
-        public TODOInfoUpdater(final ConnectedMessage message) {
-            this.application = message.getApplication();
-            this.session = SyncopeConsoleSession.get();
-            this.key = message.getKey();
-        }
-
-        @Override
-        public void run() {
-            try {
-                ThreadContext.setApplication(application);
-                ThreadContext.setSession(session);
-
-                final List<WorkflowFormTO> actual = getLastTODOs();
-                Collections.sort(actual, new WorkflowFormComparator());
-
-                if (!actual.equals(lastTODOs)) {
-                    LOG.debug("Update TODOs");
-
-                    lastTODOs.clear();
-                    lastTODOs = actual;
-
-                    WebSocketSettings settings = WebSocketSettings.Holder.get(application);
-                    WebSocketPushBroadcaster broadcaster
-                            = new WebSocketPushBroadcaster(settings.getConnectionRegistry());
-                    broadcaster.broadcast(new ConnectedMessage(application, session.getId(), key), new UpdateMessage());
-                }
-            } catch (Throwable t) {
-                LOG.error("Unexpected error while checking for updated Job info", t);
-            } finally {
-                ThreadContext.detach();
-            }
-        }
-    }
-
-    private static class UpdateMessage implements IWebSocketPushMessage, Serializable {
-
-        private static final long serialVersionUID = -824793424112532838L;
-
-    }
-
-    private class WorkflowFormComparator implements Comparator<WorkflowFormTO> {
-
-        @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());
-            }
-        }
-    }
-}