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 2015/02/13 12:44:43 UTC

[39/51] [partial] syncope git commit: [SYNCOPE-620] Re-organization completed

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
new file mode 100644
index 0000000..c47edcd
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Schema.java
@@ -0,0 +1,467 @@
+/*
+ * 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 java.lang.reflect.Field;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.PreferenceManager;
+import org.apache.syncope.client.console.commons.SchemaModalPageFactory;
+import org.apache.syncope.client.console.commons.SelectChoiceRenderer;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.JQueryUITabbedPanel;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.common.lib.to.AbstractSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Page;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * Schema WebPage.
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class Schema extends BasePage {
+
+    private static final long serialVersionUID = 8091922398776299403L;
+
+    private static final Map<SchemaType, List<String>> COL_NAMES = new HashMap<SchemaType, List<String>>() {
+
+        private static final long serialVersionUID = 3109256773218160485L;
+
+        {
+            put(SchemaType.PLAIN, Arrays.asList(new String[] { "key", "type",
+                "mandatoryCondition", "uniqueConstraint", "multivalue", "readonly" }));
+            put(SchemaType.DERIVED, Arrays.asList(new String[] { "key", "expression" }));
+            put(SchemaType.VIRTUAL, Arrays.asList(new String[] { "key", "readonly" }));
+        }
+    };
+
+    private static final Map<Map.Entry<AttributableType, SchemaType>, String> PAGINATOR_ROWS_KEYS =
+            new HashMap<Map.Entry<AttributableType, SchemaType>, String>() {
+
+                private static final long serialVersionUID = 3109256773218160485L;
+
+                {
+                    put(new SimpleEntry<>(AttributableType.CONFIGURATION, SchemaType.PLAIN),
+                            Constants.PREF_CONF_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.USER, SchemaType.PLAIN),
+                            Constants.PREF_USER_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.USER, SchemaType.DERIVED),
+                            Constants.PREF_USER_DER_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.USER, SchemaType.VIRTUAL),
+                            Constants.PREF_USER_VIR_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.MEMBERSHIP, SchemaType.PLAIN),
+                            Constants.PREF_MEMBERSHIP_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.MEMBERSHIP, SchemaType.DERIVED),
+                            Constants.PREF_MEMBERSHIP_DER_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.MEMBERSHIP, SchemaType.VIRTUAL),
+                            Constants.PREF_MEMBERSHIP_VIR_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.ROLE, SchemaType.PLAIN),
+                            Constants.PREF_ROLE_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.ROLE, SchemaType.DERIVED),
+                            Constants.PREF_ROLE_DER_SCHEMA_PAGINATOR_ROWS);
+                    put(new SimpleEntry<>(AttributableType.ROLE, SchemaType.VIRTUAL),
+                            Constants.PREF_ROLE_VIR_SCHEMA_PAGINATOR_ROWS);
+                }
+            };
+
+    private static final int WIN_WIDTH = 600;
+
+    private static final int WIN_HEIGHT = 200;
+
+    private static final int PLAIN_WIN_HEIGHT = 500;
+
+    @SpringBean
+    private SchemaRestClient restClient;
+
+    @SpringBean
+    private PreferenceManager prefMan;
+
+    private final String allowedCreateRoles = xmlRolesReader.getEntitlement("Schema", "create");
+
+    private final String allowedReadRoles = xmlRolesReader.getEntitlement("Schema", "read");
+
+    private final String allowedDeleteRoles = xmlRolesReader.getEntitlement("Schema", "delete");
+
+    public Schema() {
+        super();
+
+        for (final AttributableType attrType : AttributableType.values()) {
+            final String attrTypeAsString = attrType.name().toLowerCase();
+
+            List<ITab> tabs = new ArrayList<>();
+
+            for (final SchemaType schemaType : SchemaType.values()) {
+                if (attrType != AttributableType.CONFIGURATION || schemaType == SchemaType.PLAIN) {
+                    final String schemaTypeAsString = schemaType.name().toLowerCase();
+
+                    tabs.add(new AbstractTab(new Model<>(getString(schemaTypeAsString))) {
+
+                        private static final long serialVersionUID = -5861786415855103549L;
+
+                        @Override
+                        public WebMarkupContainer getPanel(final String panelId) {
+                            return new SchemaTypePanel(panelId, attrType, schemaType);
+                        }
+                    });
+                }
+            }
+
+            add(new JQueryUITabbedPanel(attrTypeAsString + "Tabs", tabs));
+        }
+    }
+
+    private <T extends AbstractSchemaModalPage> List<IColumn> getColumns(
+            final WebMarkupContainer webContainer, final ModalWindow modalWindow,
+            final AttributableType attributableType, final SchemaType schemaType,
+            final Collection<String> fields) {
+
+        List<IColumn> columns = new ArrayList<IColumn>();
+
+        for (final String field : fields) {
+            final Field clazzField = ReflectionUtils.findField(schemaType.getToClass(), field);
+
+            if (clazzField != null) {
+                if (clazzField.getType().equals(Boolean.class) || clazzField.getType().equals(boolean.class)) {
+                    columns.add(new AbstractColumn<AbstractSchemaTO, String>(new ResourceModel(field)) {
+
+                        private static final long serialVersionUID = 8263694778917279290L;
+
+                        @Override
+                        public void populateItem(final Item<ICellPopulator<AbstractSchemaTO>> item,
+                                final String componentId, final IModel<AbstractSchemaTO> model) {
+
+                            BeanWrapper bwi = new BeanWrapperImpl(model.getObject());
+                            Object obj = bwi.getPropertyValue(field);
+
+                            item.add(new Label(componentId, ""));
+                            item.add(new AttributeModifier("class", new Model<String>(obj.toString())));
+                        }
+
+                        @Override
+                        public String getCssClass() {
+                            return "small_fixedsize";
+                        }
+                    });
+                } else {
+                    IColumn column = new PropertyColumn(new ResourceModel(field), field, field) {
+
+                        private static final long serialVersionUID = 3282547854226892169L;
+
+                        @Override
+                        public String getCssClass() {
+                            String css = super.getCssClass();
+                            if ("key".equals(field)) {
+                                css = StringUtils.isBlank(css)
+                                        ? "medium_fixedsize"
+                                        : css + " medium_fixedsize";
+                            }
+                            return css;
+                        }
+                    };
+                    columns.add(column);
+                }
+            }
+        }
+
+        columns.add(new AbstractColumn<AbstractSchemaTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public String getCssClass() {
+                return "action";
+            }
+
+            @Override
+            public void populateItem(final Item<ICellPopulator<AbstractSchemaTO>> item, final String componentId,
+                    final IModel<AbstractSchemaTO> model) {
+
+                final AbstractSchemaTO schemaTO = model.getObject();
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, getPageReference());
+
+                panel.addWithRoles(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        modalWindow.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                AbstractSchemaModalPage page = SchemaModalPageFactory.getSchemaModalPage(
+                                        attributableType, schemaType);
+
+                                page.setSchemaModalPage(Schema.this.getPageReference(), modalWindow, schemaTO, false);
+
+                                return page;
+                            }
+                        });
+
+                        modalWindow.show(target);
+                    }
+                }, ActionType.EDIT, allowedReadRoles);
+
+                panel.addWithRoles(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+
+                        switch (schemaType) {
+                            case DERIVED:
+                                restClient.deleteDerSchema(attributableType, schemaTO.getKey());
+                                break;
+
+                            case VIRTUAL:
+                                restClient.deleteVirSchema(attributableType, schemaTO.getKey());
+                                break;
+
+                            default:
+                                restClient.deletePlainSchema(attributableType, schemaTO.getKey());
+                                break;
+                        }
+
+                        info(getString(Constants.OPERATION_SUCCEEDED));
+                        feedbackPanel.refresh(target);
+
+                        target.add(webContainer);
+                    }
+                }, ActionType.DELETE, allowedDeleteRoles);
+
+                item.add(panel);
+            }
+        });
+
+        return columns;
+    }
+
+    private Form<Void> getPaginatorForm(final WebMarkupContainer webContainer,
+            final AjaxFallbackDefaultDataTable dataTable,
+            final String formname, final SchemaTypePanel schemaTypePanel, final String rowsPerPagePrefName) {
+
+        Form<Void> form = new Form<Void>(formname);
+
+        final DropDownChoice<Integer> rowChooser = new DropDownChoice<Integer>("rowsChooser",
+                new PropertyModel<Integer>(schemaTypePanel, "pageRows"), prefMan.getPaginatorChoices(),
+                new SelectChoiceRenderer<Integer>());
+
+        rowChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                prefMan.set(getRequest(), getResponse(), rowsPerPagePrefName, rowChooser.getInput());
+                dataTable.setItemsPerPage(rowChooser.getModelObject());
+
+                target.add(webContainer);
+            }
+        });
+
+        form.add(rowChooser);
+
+        return form;
+    }
+
+    private <T extends AbstractSchemaModalPage> AjaxLink<Void> getCreateSchemaLink(final ModalWindow modalWindow,
+            final AttributableType attrType, final SchemaType schemaType, final String winLinkName) {
+
+        AjaxLink<Void> link = new ClearIndicatingAjaxLink<Void>(winLinkName, getPageReference()) {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            protected void onClickInternal(final AjaxRequestTarget target) {
+                modalWindow.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+                        T page = SchemaModalPageFactory.getSchemaModalPage(attrType, schemaType);
+                        page.setSchemaModalPage(Schema.this.getPageReference(), modalWindow, null, true);
+
+                        return page;
+                    }
+                });
+
+                modalWindow.show(target);
+            }
+        };
+
+        MetaDataRoleAuthorizationStrategy.authorize(link, ENABLE, allowedCreateRoles);
+
+        return link;
+
+    }
+
+    private class SchemaProvider extends SortableDataProvider<AbstractSchemaTO, String> {
+
+        private static final long serialVersionUID = -185944053385660794L;
+
+        private final SortableDataProviderComparator<AbstractSchemaTO> comparator;
+
+        private final AttributableType attrType;
+
+        private final SchemaType schemaType;
+
+        public SchemaProvider(final AttributableType attrType, final SchemaType schemaType) {
+            super();
+
+            this.attrType = attrType;
+            this.schemaType = schemaType;
+
+            // Default sorting
+            setSort("key", SortOrder.ASCENDING);
+
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<AbstractSchemaTO> iterator(final long first, final long count) {
+            @SuppressWarnings("unchecked")
+            List<AbstractSchemaTO> list =
+                    (List<AbstractSchemaTO>) restClient.getSchemas(this.attrType, this.schemaType);
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.getSchemas(this.attrType, this.schemaType).size();
+        }
+
+        @Override
+        public IModel<AbstractSchemaTO> model(final AbstractSchemaTO object) {
+            return new CompoundPropertyModel<AbstractSchemaTO>(object);
+        }
+    }
+
+    private class SchemaTypePanel extends Panel {
+
+        private static final long serialVersionUID = 2854050613688773575L;
+
+        private int pageRows;
+
+        private final AttributableType attrType;
+
+        private final SchemaType schemaType;
+
+        public SchemaTypePanel(final String id, final AttributableType attrType, final SchemaType schemaType) {
+            super(id);
+
+            this.attrType = attrType;
+            this.schemaType = schemaType;
+
+            setup();
+        }
+
+        private void setup() {
+            ModalWindow editSchemaWin = new ModalWindow("editSchemaWin");
+            editSchemaWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+            editSchemaWin.setInitialWidth(WIN_WIDTH);
+            if (schemaType == SchemaType.PLAIN) {
+                editSchemaWin.setInitialHeight(PLAIN_WIN_HEIGHT);
+            } else {
+                editSchemaWin.setInitialHeight(WIN_HEIGHT);
+            }
+            editSchemaWin.setCookieName("editSchemaWin");
+            editSchemaWin.setMarkupId("editSchemaWin");
+            add(editSchemaWin);
+
+            WebMarkupContainer schemaWrapContainer = new WebMarkupContainer("schemaWrapContainer");
+            schemaWrapContainer.setOutputMarkupId(true);
+            if (schemaType != SchemaType.VIRTUAL) {
+                schemaWrapContainer.add(new AttributeModifier("style", "width:auto;"));
+            }
+            add(schemaWrapContainer);
+
+            WebMarkupContainer schemaContainer = new WebMarkupContainer("schemaContainer");
+            schemaContainer.setOutputMarkupId(true);
+            schemaWrapContainer.add(schemaContainer);
+            setWindowClosedCallback(editSchemaWin, schemaContainer);
+
+            final String paginatorRowsKey = PAGINATOR_ROWS_KEYS.get(
+                    new SimpleEntry<AttributableType, SchemaType>(attrType, schemaType));
+            pageRows = prefMan.getPaginatorRows(getRequest(), paginatorRowsKey);
+
+            List<IColumn> tableCols = getColumns(schemaContainer, editSchemaWin, attrType,
+                    schemaType, COL_NAMES.get(schemaType));
+            final AjaxFallbackDefaultDataTable table = new AjaxFallbackDefaultDataTable("datatable", tableCols,
+                    new SchemaProvider(attrType, schemaType), pageRows);
+            table.setOutputMarkupId(true);
+            schemaContainer.add(table);
+
+            schemaWrapContainer.add(getPaginatorForm(schemaContainer, table, "paginatorForm", this, paginatorRowsKey));
+
+            add(getCreateSchemaLink(editSchemaWin, attrType, schemaType, "createSchemaLink"));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestionModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestionModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestionModalPage.java
new file mode 100644
index 0000000..12bfc2b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/SecurityQuestionModalPage.java
@@ -0,0 +1,111 @@
+/*
+ * 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.commons.Constants;
+import org.apache.syncope.client.console.rest.SecurityQuestionRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+class SecurityQuestionModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -6709838862698327502L;
+
+    @SpringBean
+    private SecurityQuestionRestClient restClient;
+
+    public SecurityQuestionModalPage(final PageReference pageRef, final ModalWindow window,
+            final SecurityQuestionTO securityQuestionTO, final boolean createFlag) {
+
+        final Form<SecurityQuestionTO> form =
+                new Form<SecurityQuestionTO>(FORM, new CompoundPropertyModel<SecurityQuestionTO>(securityQuestionTO));
+
+        final AjaxTextFieldPanel contentFieldPanel =
+                new AjaxTextFieldPanel("content", "content", new PropertyModel<String>(securityQuestionTO, "content"));
+        contentFieldPanel.setRequired(true);
+        form.add(contentFieldPanel);
+
+        AjaxButton submit = new IndicatingAjaxButton(APPLY, new Model<String>(getString(SUBMIT))) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                try {
+                    if (createFlag) {
+                        restClient.create(securityQuestionTO);
+                    } else {
+                        restClient.update(securityQuestionTO);
+                    }
+                    info(getString(Constants.OPERATION_SUCCEEDED));
+
+                    Configuration callerPage = (Configuration) pageRef.getPage();
+                    callerPage.setModalResult(true);
+
+                    window.close(target);
+                } catch (SyncopeClientException scee) {
+                    error(getString(Constants.ERROR) + ": " + scee.getMessage());
+                    feedbackPanel.refresh(target);
+                }
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                feedbackPanel.refresh(target);
+            }
+        };
+
+        final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+        };
+
+        cancel.setDefaultFormProcessing(false);
+
+        String allowedRoles = createFlag
+                ? xmlRolesReader.getEntitlement("SecurityQuestion", "create")
+                : xmlRolesReader.getEntitlement("SecurityQuestion", "update");
+        MetaDataRoleAuthorizationStrategy.authorize(submit, ENABLE, allowedRoles);
+
+        form.add(submit);
+        form.setDefaultButton(submit);
+
+        form.add(cancel);
+
+        add(form);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/StatusModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/StatusModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/StatusModalPage.java
new file mode 100644
index 0000000..9d0ede2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/StatusModalPage.java
@@ -0,0 +1,644 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.status.AbstractStatusBeanProvider;
+import org.apache.syncope.client.console.commons.status.ConnObjectWrapper;
+import org.apache.syncope.client.console.commons.status.Status;
+import org.apache.syncope.client.console.commons.status.StatusBean;
+import org.apache.syncope.client.console.commons.status.StatusUtils;
+import org.apache.syncope.client.console.panels.ActionDataTablePanel;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxButton;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class StatusModalPage<T extends AbstractSubjectTO> extends AbstractStatusModalPage {
+
+    private static final long serialVersionUID = -9148734710505211261L;
+
+    private final AbstractSubjectTO subjectTO;
+
+    private int rowsPerPage = 10;
+
+    final StatusUtils statusUtils;
+
+    final boolean statusOnly;
+
+    // --------------------------------
+    // password management fields ..
+    // --------------------------------
+    final ClearIndicatingAjaxButton cancel;
+
+    final WebMarkupContainer pwdMgt;
+
+    final Form<?> pwdMgtForm;
+
+    final AjaxCheckBoxPanel changepwd;
+
+    final PasswordTextField password;
+
+    final PasswordTextField confirm;
+    // --------------------------------
+
+    final PageReference pageRef;
+
+    final ModalWindow window;
+
+    final ActionDataTablePanel<StatusBean, String> table;
+
+    final List<IColumn<StatusBean, String>> columns;
+
+    public StatusModalPage(
+            final PageReference pageRef,
+            final ModalWindow window,
+            final AbstractSubjectTO attributableTO) {
+
+        this(pageRef, window, attributableTO, false);
+    }
+
+    public StatusModalPage(
+            final PageReference pageRef,
+            final ModalWindow window,
+            final AbstractSubjectTO subjectTO,
+            final boolean statusOnly) {
+
+        super();
+
+        this.pageRef = pageRef;
+        this.window = window;
+        this.statusOnly = statusOnly;
+        this.subjectTO = subjectTO;
+
+        statusUtils = new StatusUtils(subjectTO instanceof UserTO ? userRestClient : roleRestClient);
+
+        add(new Label("displayName", subjectTO.getKey() + " "
+                + (subjectTO instanceof UserTO ? ((UserTO) subjectTO).getUsername() : ((RoleTO) subjectTO).getName())));
+
+        columns = new ArrayList<>();
+        columns.add(new AbstractColumn<StatusBean, String>(
+                new StringResourceModel("resourceName", this, null, "Resource name"), "resourceName") {
+
+                    private static final long serialVersionUID = 2054811145491901166L;
+
+                    @Override
+                    public void populateItem(
+                            final Item<ICellPopulator<StatusBean>> cellItem,
+                            final String componentId,
+                            final IModel<StatusBean> model) {
+
+                                cellItem.add(new Label(componentId, model.getObject().getResourceName()) {
+
+                                    private static final long serialVersionUID = 8432079838783825801L;
+
+                                    @Override
+                                    protected void onComponentTag(final ComponentTag tag) {
+                                        if (model.getObject().isLinked()) {
+                                            super.onComponentTag(tag);
+                                        } else {
+                                            tag.put("style", "color: #DDDDDD");
+                                        }
+                                    }
+                                });
+                            }
+                });
+
+        columns.add(new PropertyColumn<StatusBean, String>(
+                new StringResourceModel("accountLink", this, null, "Account link"), "accountLink", "accountLink"));
+
+        columns.add(new AbstractColumn<StatusBean, String>(
+                new StringResourceModel("status", this, null, "")) {
+
+                    private static final long serialVersionUID = -3503023501954863131L;
+
+                    @Override
+                    public String getCssClass() {
+                        return "action";
+                    }
+
+                    @Override
+                    public void populateItem(
+                            final Item<ICellPopulator<StatusBean>> cellItem,
+                            final String componentId,
+                            final IModel<StatusBean> model) {
+
+                                if (model.getObject().isLinked()) {
+                                    cellItem.add(statusUtils.getStatusImagePanel(componentId, model.getObject().
+                                                    getStatus()));
+                                } else {
+                                    cellItem.add(new Label(componentId, ""));
+                                }
+                            }
+                });
+
+        table = new ActionDataTablePanel<StatusBean, String>(
+                "resourceDatatable",
+                columns,
+                (ISortableDataProvider<StatusBean, String>) new AttributableStatusProvider(),
+                rowsPerPage,
+                pageRef) {
+
+                    private static final long serialVersionUID = 6510391461033818316L;
+
+                    @Override
+                    public boolean isElementEnabled(final StatusBean element) {
+                        return !statusOnly || element.getStatus() != Status.OBJECT_NOT_FOUND;
+                    }
+                };
+        table.setOutputMarkupId(true);
+
+        final String pageId = subjectTO instanceof RoleTO ? "Roles" : "Users";
+
+        final Fragment pwdMgtFragment = new Fragment("pwdMgtFields", "pwdMgtFragment", this);
+        addOrReplace(pwdMgtFragment);
+
+        pwdMgt = new WebMarkupContainer("pwdMgt");
+        pwdMgtFragment.add(pwdMgt.setOutputMarkupId(true));
+
+        pwdMgtForm = new Form("pwdMgtForm");
+        pwdMgtForm.setVisible(false).setEnabled(false);
+        pwdMgt.add(pwdMgtForm);
+
+        password = new PasswordTextField("password", new Model<String>());
+        pwdMgtForm.add(password.setRequired(false).setEnabled(false));
+
+        confirm = new PasswordTextField("confirm", new Model<String>());
+        pwdMgtForm.add(confirm.setRequired(false).setEnabled(false));
+
+        changepwd = new AjaxCheckBoxPanel("changepwd", "changepwd", new Model<Boolean>(false));
+        pwdMgtForm.add(changepwd.setModelObject(false));
+        pwdMgtForm.add(new Label("changePwdLabel", new ResourceModel("changePwdLabel", "Password propagation")));
+
+        changepwd.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                password.setEnabled(changepwd.getModelObject());
+                confirm.setEnabled(changepwd.getModelObject());
+                target.add(pwdMgt);
+            }
+        });
+
+        cancel = new ClearIndicatingAjaxButton("cancel", new ResourceModel("cancel"), pageRef) {
+
+            private static final long serialVersionUID = -2341391430136818026L;
+
+            @Override
+            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                // ignore
+                window.close(target);
+            }
+        }.feedbackPanelAutomaticReload(false);
+
+        pwdMgtForm.add(cancel);
+
+        final ClearIndicatingAjaxButton goon =
+                new ClearIndicatingAjaxButton("continue", new ResourceModel("continue"), pageRef) {
+
+                    private static final long serialVersionUID = -2341391430136818027L;
+
+                    @Override
+                    protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                        // none
+                    }
+                };
+
+        pwdMgtForm.add(goon);
+
+        if (statusOnly) {
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        userRestClient.reactivate(
+                                subjectTO.getETagValue(),
+                                subjectTO.getKey(),
+                                new ArrayList<>(table.getModelObject()));
+
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+
+                        window.close(target);
+                    } catch (Exception e) {
+                        LOG.error("Error enabling resources", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.REACTIVATE, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        userRestClient.suspend(
+                                subjectTO.getETagValue(),
+                                subjectTO.getKey(),
+                                new ArrayList<>(table.getModelObject()));
+
+                        if (pageRef.getPage() instanceof BasePage) {
+                            ((BasePage) pageRef.getPage()).setModalResult(true);
+                        }
+
+                        window.close(target);
+                    } catch (Exception e) {
+                        LOG.error("Error disabling resources", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.SUSPEND, pageId);
+        } else {
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        if (subjectTO instanceof UserTO) {
+                            userRestClient.unlink(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        } else {
+                            roleRestClient.unlink(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        }
+
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                        window.close(target);
+                    } catch (Exception e) {
+                        LOG.error("Error unlinking resources", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.UNLINK, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        if (subjectTO instanceof UserTO) {
+                            userRestClient.link(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        } else {
+                            roleRestClient.link(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        }
+
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                        window.close(target);
+                    } catch (Exception e) {
+                        LOG.error("Error linking resources", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.LINK, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        BulkActionResult bulkActionResult;
+                        if (subjectTO instanceof UserTO) {
+                            bulkActionResult = userRestClient.deprovision(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        } else {
+                            bulkActionResult = roleRestClient.deprovision(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        }
+
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                        loadBulkActionResultPage(table.getModelObject(), bulkActionResult);
+                    } catch (Exception e) {
+                        LOG.error("Error de-provisioning user", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.DEPROVISION, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+
+                    if (subjectTO instanceof UserTO) {
+                        StatusModalPage.this.passwordManagement(
+                                target, ResourceAssociationActionType.PROVISION, table.getModelObject());
+                    } else {
+                        try {
+                            final BulkActionResult bulkActionResult = roleRestClient.provision(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+
+                            ((BasePage) pageRef.getPage()).setModalResult(true);
+                            loadBulkActionResultPage(table.getModelObject(), bulkActionResult);
+                        } catch (Exception e) {
+                            LOG.error("Error provisioning user", e);
+                            error(getString(Constants.ERROR) + ": " + e.getMessage());
+                            feedbackPanel.refresh(target);
+                        }
+                    }
+                }
+            }.feedbackPanelAutomaticReload(!(subjectTO instanceof UserTO)), ActionLink.ActionType.PROVISION, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    try {
+                        final BulkActionResult bulkActionResult;
+                        if (subjectTO instanceof UserTO) {
+                            bulkActionResult = userRestClient.unassign(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        } else {
+                            bulkActionResult = roleRestClient.unassign(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+                        }
+
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                        loadBulkActionResultPage(table.getModelObject(), bulkActionResult);
+                    } catch (Exception e) {
+                        LOG.error("Error unassigning resources", e);
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        feedbackPanel.refresh(target);
+                    }
+                }
+            }, ActionLink.ActionType.UNASSIGN, pageId);
+
+            table.addAction(new ActionLink() {
+
+                private static final long serialVersionUID = -3722207913631435501L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    if (subjectTO instanceof UserTO) {
+                        StatusModalPage.this.passwordManagement(
+                                target, ResourceAssociationActionType.ASSIGN, table.getModelObject());
+                    } else {
+                        try {
+                            final BulkActionResult bulkActionResult = roleRestClient.assign(
+                                    subjectTO.getETagValue(),
+                                    subjectTO.getKey(),
+                                    new ArrayList<>(table.getModelObject()));
+
+                            ((BasePage) pageRef.getPage()).setModalResult(true);
+                            loadBulkActionResultPage(table.getModelObject(), bulkActionResult);
+                        } catch (Exception e) {
+                            LOG.error("Error assigning resources", e);
+                            error(getString(Constants.ERROR) + ": " + e.getMessage());
+                            feedbackPanel.refresh(target);
+                        }
+                    }
+                }
+            }.feedbackPanelAutomaticReload(!(subjectTO instanceof UserTO)), ActionLink.ActionType.ASSIGN, pageId);
+        }
+
+        table.addCancelButton(window);
+        add(table);
+    }
+
+    private class AttributableStatusProvider extends AbstractStatusBeanProvider {
+
+        private static final long serialVersionUID = 4586969457669796621L;
+
+        public AttributableStatusProvider() {
+            super(statusOnly ? "resourceName" : "accountLink");
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public List<StatusBean> getStatusBeans() {
+            final List<String> resources = new ArrayList<>();
+            for (ResourceTO resourceTO : resourceRestClient.getAll()) {
+                resources.add(resourceTO.getKey());
+            }
+
+            final List<ConnObjectWrapper> connObjects = statusUtils.getConnectorObjects(subjectTO);
+
+            final List<StatusBean> statusBeans = new ArrayList<StatusBean>(connObjects.size() + 1);
+
+            for (ConnObjectWrapper entry : connObjects) {
+                final StatusBean statusBean = statusUtils.getStatusBean(
+                        subjectTO,
+                        entry.getResourceName(),
+                        entry.getConnObjectTO(),
+                        subjectTO instanceof RoleTO);
+
+                statusBeans.add(statusBean);
+                resources.remove(entry.getResourceName());
+            }
+
+            if (statusOnly) {
+                final StatusBean syncope = new StatusBean(subjectTO, "Syncope");
+
+                syncope.setAccountLink(((UserTO) subjectTO).getUsername());
+
+                Status syncopeStatus = Status.UNDEFINED;
+                if (((UserTO) subjectTO).getStatus() != null) {
+                    try {
+                        syncopeStatus = Status.valueOf(((UserTO) subjectTO).getStatus().toUpperCase());
+                    } catch (IllegalArgumentException e) {
+                        LOG.warn("Unexpected status found: {}", ((UserTO) subjectTO).getStatus(), e);
+                    }
+                }
+                syncope.setStatus(syncopeStatus);
+
+                statusBeans.add(syncope);
+            } else {
+                for (String resource : resources) {
+                    final StatusBean statusBean = statusUtils.getStatusBean(
+                            subjectTO,
+                            resource,
+                            null,
+                            subjectTO instanceof RoleTO);
+
+                    statusBean.setLinked(false);
+                    statusBeans.add(statusBean);
+                }
+            }
+
+            return statusBeans;
+        }
+    }
+
+    private void passwordManagement(
+            final AjaxRequestTarget target,
+            final ResourceAssociationActionType type,
+            final Collection<StatusBean> selection) {
+
+        final ClearIndicatingAjaxButton goon =
+                new ClearIndicatingAjaxButton("continue", new ResourceModel("continue", "Continue"), pageRef) {
+
+                    private static final long serialVersionUID = -2341391430136818027L;
+
+                    @Override
+                    protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                        try {
+                            if (StringUtils.isNotBlank(password.getModelObject())
+                            && !password.getModelObject().equals(confirm.getModelObject())) {
+                                throw new Exception(getString("passwordMismatch"));
+                            }
+
+                            final BulkActionResult bulkActionResult;
+                            switch (type) {
+                                case ASSIGN:
+                                    bulkActionResult = userRestClient.assign(
+                                            subjectTO.getETagValue(),
+                                            subjectTO.getKey(),
+                                            new ArrayList<>(selection),
+                                            changepwd.getModelObject(),
+                                            password.getModelObject());
+                                    break;
+                                case PROVISION:
+                                    bulkActionResult = userRestClient.provision(
+                                            subjectTO.getETagValue(),
+                                            subjectTO.getKey(),
+                                            new ArrayList<>(selection),
+                                            changepwd.getModelObject(),
+                                            password.getModelObject());
+                                    break;
+                                default:
+                                    bulkActionResult = null;
+                                // ignore
+                            }
+
+                            ((BasePage) pageRef.getPage()).setModalResult(true);
+
+                            if (bulkActionResult != null) {
+                                loadBulkActionResultPage(selection, bulkActionResult);
+                            } else {
+
+                                target.add(((BasePage) pageRef.getPage()).getFeedbackPanel());
+                                window.close(target);
+                            }
+                        } catch (Exception e) {
+                            LOG.error("Error provisioning resources", e);
+                            error(getString(Constants.ERROR) + ": " + e.getMessage());
+                            feedbackPanel.refresh(target);
+                        }
+                    }
+                }.feedbackPanelAutomaticReload(false);
+
+        pwdMgtForm.addOrReplace(goon);
+
+        table.setVisible(false);
+        pwdMgtForm.setVisible(true).setEnabled(true);
+
+        target.add(table);
+        target.add(pwdMgt);
+    }
+
+    private void loadBulkActionResultPage(
+            final Collection<StatusBean> selection, final BulkActionResult bulkActionResult) {
+        final List<String> resources = new ArrayList<String>(selection.size());
+        for (StatusBean statusBean : selection) {
+            resources.add(statusBean.getResourceName());
+        }
+
+        final List<ConnObjectWrapper> connObjects =
+                statusUtils.getConnectorObjects(Collections.singletonList(subjectTO), resources);
+
+        final List<StatusBean> statusBeans = new ArrayList<StatusBean>(connObjects.size());
+
+        for (ConnObjectWrapper entry : connObjects) {
+            final StatusBean statusBean = statusUtils.getStatusBean(
+                    subjectTO,
+                    entry.getResourceName(),
+                    entry.getConnObjectTO(),
+                    subjectTO instanceof RoleTO);
+
+            statusBeans.add(statusBean);
+        }
+
+        setResponsePage(new BulkActionResultModalPage<StatusBean, String>(
+                window,
+                statusBeans,
+                columns,
+                bulkActionResult,
+                "resourceName"));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/SyncTaskModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/SyncTaskModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/SyncTaskModalPage.java
new file mode 100644
index 0000000..544f670
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/SyncTaskModalPage.java
@@ -0,0 +1,73 @@
+/*
+ * 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 java.util.List;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.SyncTaskTO;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.model.PropertyModel;
+
+/**
+ * Modal window with Sync Task form.
+ */
+public class SyncTaskModalPage extends AbstractSyncTaskModalPage {
+
+    private static final long serialVersionUID = 2148403203517274669L;
+
+    public SyncTaskModalPage(final ModalWindow window, final SyncTaskTO taskTO, final PageReference pageRef) {
+
+        super(window, taskTO, pageRef);
+
+        // set default Matching rule
+        ((DropDownChoice) matchingRule.getField()).setDefaultModelObject(taskTO.getMatchingRule() == null
+                ? MatchingRule.UPDATE
+                : taskTO.getMatchingRule());
+        profile.add(matchingRule);
+
+        // set default Unmatching rule
+        ((DropDownChoice) unmatchingRule.getField()).setDefaultModelObject(taskTO.getUnmatchingRule() == null
+                ? UnmatchingRule.PROVISION
+                : taskTO.getUnmatchingRule());
+        profile.add(unmatchingRule);
+
+        final AjaxCheckBoxPanel fullReconciliation = new AjaxCheckBoxPanel("fullReconciliation",
+                getString("fullReconciliation"), new PropertyModel<Boolean>(taskTO, "fullReconciliation"));
+        profile.add(fullReconciliation);
+    }
+
+    @Override
+    protected List<String> getSyncActions() {
+        return taskRestClient.getSyncActionsClasses();
+    }
+
+    @Override
+    public void submitAction(final SchedTaskTO taskTO) {
+        if (taskTO.getKey() > 0) {
+            taskRestClient.updateSyncTask((SyncTaskTO) taskTO);
+        } else {
+            taskRestClient.createSyncTask((SyncTaskTO) taskTO);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/TaskModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/TaskModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/TaskModalPage.java
new file mode 100644
index 0000000..c251d87
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/TaskModalPage.java
@@ -0,0 +1,253 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+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.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.NotificationTaskTO;
+import org.apache.syncope.common.lib.to.PropagationTaskTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.SyncTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
+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.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+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.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.springframework.util.StringUtils;
+
+/**
+ * Modal window with Task form (to stop and start execution).
+ */
+public abstract class TaskModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -4110576026663173545L;
+
+    protected WebMarkupContainer profile;
+
+    protected WebMarkupContainer executions;
+
+    protected Form<AbstractTaskTO> form;
+
+    public TaskModalPage(final AbstractTaskTO taskTO) {
+        final ModalWindow taskExecMessageWin = new ModalWindow("taskExecMessageWin");
+        taskExecMessageWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        taskExecMessageWin.setCookieName("task-exec-message-win-modal");
+        add(taskExecMessageWin);
+
+        form = new Form<>(FORM);
+        form.setModel(new CompoundPropertyModel<>(taskTO));
+        add(form);
+
+        profile = new WebMarkupContainer("profile");
+        profile.setOutputMarkupId(true);
+        form.add(profile);
+
+        executions = new WebMarkupContainer("executionContainer");
+        executions.setOutputMarkupId(true);
+        form.add(executions);
+
+        final Label idLabel = new Label("idLabel", new ResourceModel("key"));
+        profile.add(idLabel);
+
+        final AjaxTextFieldPanel id =
+                new AjaxTextFieldPanel("key", getString("key"), new PropertyModel<String>(taskTO, "key"));
+
+        id.setEnabled(false);
+        profile.add(id);
+
+        final List<IColumn<TaskExecTO, String>> columns = new ArrayList<>();
+
+        final int paginatorRows = 10;
+
+        columns.add(new PropertyColumn<TaskExecTO, String>(new ResourceModel("key"), "key", "key"));
+        columns.add(new DatePropertyColumn<TaskExecTO>(new ResourceModel("startDate"), "startDate", "startDate"));
+        columns.add(new DatePropertyColumn<TaskExecTO>(new ResourceModel("endDate"), "endDate", "endDate"));
+        columns.add(new PropertyColumn<TaskExecTO, String>(new ResourceModel("status"), "status", "status"));
+        columns.add(new ActionColumn<TaskExecTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public ActionLinksPanel getActions(final String componentId, final IModel<TaskExecTO> model) {
+
+                final TaskExecTO taskExecutionTO = model.getObject();
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        taskExecMessageWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new ExecMessageModalPage(model.getObject().getMessage());
+                            }
+                        });
+                        taskExecMessageWin.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, TASKS, StringUtils.hasText(model.getObject().getMessage()));
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        try {
+                            taskRestClient.deleteExecution(taskExecutionTO.getKey());
+
+                            taskTO.getExecutions().remove(taskExecutionTO);
+
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+                        } catch (SyncopeClientException scce) {
+                            error(scce.getMessage());
+                        }
+
+                        feedbackPanel.refresh(target);
+                        target.add(executions);
+                    }
+                }, ActionLink.ActionType.DELETE, TASKS);
+
+                return panel;
+            }
+
+            @Override
+            public Component getHeader(final String componentId) {
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        if (target != null) {
+                            final AjaxFallbackDefaultDataTable<TaskExecTO, String> currentTable =
+                                    new AjaxFallbackDefaultDataTable<TaskExecTO, String>("executionsTable", columns,
+                                            new TaskExecutionsProvider(getCurrentTaskExecution(taskTO)), paginatorRows);
+                            currentTable.setOutputMarkupId(true);
+                            target.add(currentTable);
+                            executions.addOrReplace(currentTable);
+                        }
+                    }
+                }, ActionLink.ActionType.RELOAD, TASKS, "list");
+
+                return panel;
+            }
+        });
+
+        final AjaxFallbackDefaultDataTable<TaskExecTO, String> table =
+                new AjaxFallbackDefaultDataTable<TaskExecTO, String>("executionsTable", columns,
+                        new TaskExecutionsProvider(getCurrentTaskExecution(taskTO)), paginatorRows);
+
+        executions.add(table);
+    }
+
+    protected static class TaskExecutionsProvider extends SortableDataProvider<TaskExecTO, String> {
+
+        private static final long serialVersionUID = 8943636537120648961L;
+
+        private SortableDataProviderComparator<TaskExecTO> comparator;
+
+        private AbstractTaskTO taskTO;
+
+        public TaskExecutionsProvider(final AbstractTaskTO taskTO) {
+            //Default sorting
+            this.taskTO = taskTO;
+            setSort("startDate", SortOrder.DESCENDING);
+            comparator = new SortableDataProviderComparator<TaskExecTO>(this);
+        }
+
+        @Override
+        public Iterator<TaskExecTO> iterator(final long first, final long count) {
+
+            List<TaskExecTO> list = taskTO.getExecutions();
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return taskTO.getExecutions().size();
+        }
+
+        @Override
+        public IModel<TaskExecTO> model(final TaskExecTO taskExecution) {
+
+            return new AbstractReadOnlyModel<TaskExecTO>() {
+
+                private static final long serialVersionUID = 7485475149862342421L;
+
+                @Override
+                public TaskExecTO getObject() {
+                    return taskExecution;
+                }
+            };
+        }
+    }
+
+    private AbstractTaskTO getCurrentTaskExecution(final AbstractTaskTO taskTO) {
+        final AbstractTaskTO currentTask = taskTO.getKey() == 0
+                ? taskTO
+                : taskTO instanceof PropagationTaskTO
+                        ? taskRestClient.readPropagationTask(taskTO.getKey())
+                        : taskTO instanceof NotificationTaskTO
+                                ? taskRestClient.readNotificationTask(taskTO.getKey())
+                                : taskTO instanceof SyncTaskTO
+                                        ? taskRestClient.readSchedTask(SyncTaskTO.class, taskTO.getKey())
+                                        : taskRestClient.readSchedTask(SchedTaskTO.class, taskTO.getKey());
+
+        taskTO.getExecutions().clear();
+        taskTO.getExecutions().addAll(currentTask.getExecutions());
+        return taskTO;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/Tasks.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Tasks.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Tasks.java
new file mode 100644
index 0000000..296c365
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Tasks.java
@@ -0,0 +1,230 @@
+/*
+ * 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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.AjaxDataTablePanel;
+import org.apache.syncope.client.console.panels.NotificationTasks;
+import org.apache.syncope.client.console.panels.PropagationTasks;
+import org.apache.syncope.client.console.panels.PushTasksPanel;
+import org.apache.syncope.client.console.panels.SchedTasks;
+import org.apache.syncope.client.console.panels.SyncTasksPanel;
+import org.apache.syncope.client.console.rest.BaseRestClient;
+import org.apache.syncope.client.console.rest.TaskRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.to.TaskExecTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+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.ISortableDataProvider;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+
+public class Tasks extends BasePage {
+
+    private static final long serialVersionUID = 5289215853622289061L;
+
+    public Tasks() {
+        super();
+
+        add(new PropagationTasks("propagation", getPageReference()));
+        add(new NotificationTasks("notification", getPageReference()));
+        add(new SchedTasks("sched", getPageReference()));
+        add(new SyncTasksPanel("sync", getPageReference()));
+        add(new PushTasksPanel("push", getPageReference()));
+
+        getPageReference();
+    }
+
+    @Override
+    public void setWindowClosedCallback(final ModalWindow window, final WebMarkupContainer container) {
+
+        super.setWindowClosedCallback(window, container);
+    }
+
+    public static class TaskExecutionsProvider extends SortableDataProvider<TaskExecTO, String> {
+
+        private static final long serialVersionUID = -5401263348984206145L;
+
+        private SortableDataProviderComparator<TaskExecTO> comparator;
+
+        private AbstractTaskTO taskTO;
+
+        public TaskExecutionsProvider(final AbstractTaskTO taskTO) {
+            super();
+
+            //Default sorting
+            this.taskTO = taskTO;
+            setSort("startDate", SortOrder.DESCENDING);
+            comparator = new SortableDataProviderComparator<TaskExecTO>(this);
+        }
+
+        @Override
+        public Iterator<TaskExecTO> iterator(final long first, final long count) {
+
+            List<TaskExecTO> list = getTaskDB();
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return getTaskDB().size();
+        }
+
+        @Override
+        public IModel<TaskExecTO> model(final TaskExecTO taskExecution) {
+
+            return new AbstractReadOnlyModel<TaskExecTO>() {
+
+                private static final long serialVersionUID = 7485475149862342421L;
+
+                @Override
+                public TaskExecTO getObject() {
+                    return taskExecution;
+                }
+            };
+        }
+
+        public List<TaskExecTO> getTaskDB() {
+            return taskTO.getExecutions();
+        }
+    }
+
+    public static class TasksProvider<T extends AbstractTaskTO> extends SortableDataProvider<T, String> {
+
+        private static final long serialVersionUID = -20112718133295756L;
+
+        private SortableDataProviderComparator<T> comparator;
+
+        private TaskRestClient restClient;
+
+        private int paginatorRows;
+
+        private String id;
+
+        private Class<T> reference;
+
+        public TasksProvider(
+                final TaskRestClient restClient, final int paginatorRows, final String id, final Class<T> reference) {
+
+            super();
+
+            //Default sorting
+            setSort("key", SortOrder.DESCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+            this.paginatorRows = paginatorRows;
+            this.restClient = restClient;
+            this.id = id;
+            this.reference = reference;
+        }
+
+        @Override
+        public Iterator<T> iterator(final long first, final long count) {
+            final List<T> tasks = new ArrayList<>();
+
+            final int page = ((int) first / paginatorRows);
+
+            for (T task : restClient.list(reference, (page < 0 ? 0 : page) + 1, paginatorRows, getSort())) {
+                if (task instanceof SchedTaskTO && ((SchedTaskTO) task).getLastExec() == null
+                        && task.getExecutions() != null && !task.getExecutions().isEmpty()) {
+
+                    Collections.sort(task.getExecutions(), new Comparator<TaskExecTO>() {
+
+                        @Override
+                        public int compare(final TaskExecTO left, final TaskExecTO right) {
+                            return left.getStartDate().compareTo(right.getStartDate());
+                        }
+                    });
+
+                    ((SchedTaskTO) task).setLastExec(task.getExecutions().get(task.getExecutions().size() - 1).
+                            getStartDate());
+                }
+                tasks.add(task);
+            }
+
+            Collections.sort(tasks, comparator);
+            return tasks.iterator();
+        }
+
+        @Override
+        public long size() {
+            return restClient.count(id);
+        }
+
+        @Override
+        public IModel<T> model(final T object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+
+    /**
+     * Update task table.
+     *
+     * @param columns columns.
+     * @param dataProvider data provider.
+     * @param container container.
+     * @param currentPage current page index.
+     * @param pageRef page reference
+     * @param restClient syncope base rest client
+     * @return data table.
+     */
+    public static AjaxDataTablePanel<AbstractTaskTO, String> updateTaskTable(
+            final List<IColumn<AbstractTaskTO, String>> columns,
+            final TasksProvider<? extends AbstractTaskTO> dataProvider,
+            final WebMarkupContainer container,
+            final int currentPage,
+            final PageReference pageRef,
+            final BaseRestClient restClient) {
+
+        @SuppressWarnings("unchecked")
+        final AjaxDataTablePanel<AbstractTaskTO, String> table = new AjaxDataTablePanel<>(
+                "datatable",
+                columns,
+                (ISortableDataProvider<AbstractTaskTO, String>) dataProvider,
+                dataProvider.paginatorRows,
+                Arrays.asList(new ActionLink.ActionType[] {
+                    ActionLink.ActionType.DELETE, ActionLink.ActionType.DRYRUN, ActionLink.ActionType.EXECUTE }),
+                restClient,
+                "key",
+                TASKS,
+                pageRef);
+
+        table.setCurrentPage(currentPage);
+        table.setOutputMarkupId(true);
+
+        container.addOrReplace(table);
+
+        return table;
+    }
+}