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/06/10 14:34:54 UTC

[19/27] syncope git commit: [SYNCOPE-156] working with resource and connector topology

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.java
new file mode 100644
index 0000000..0af8a0b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.client.console.PreferenceManager;
+import org.apache.syncope.client.console.commons.AttributableDataProvider;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.AbstractBasePage;
+import org.apache.syncope.client.console.rest.AbstractSubjectRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.event.IEventSource;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+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.model.PropertyModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractSearchResultPanel extends Panel implements IEventSource {
+
+    private static final long serialVersionUID = -9170191461250434024L;
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractSearchResultPanel.class);
+
+    /**
+     * Edit modal window height.
+     */
+    private static final int EDIT_MODAL_WIN_HEIGHT = 550;
+
+    /**
+     * Edit modal window width.
+     */
+    private static final int EDIT_MODAL_WIN_WIDTH = 800;
+
+    /**
+     * Schemas to be shown modal window height.
+     */
+    private static final int DISPLAYATTRS_MODAL_WIN_HEIGHT = 550;
+
+    /**
+     * Schemas to be shown modal window width.
+     */
+    private static final int DISPLAYATTRS_MODAL_WIN_WIDTH = 550;
+
+    /**
+     * Schemas to be shown modal window height.
+     */
+    private static final int STATUS_MODAL_WIN_HEIGHT = 500;
+
+    /**
+     * Schemas to be shown modal window width.
+     */
+    private static final int STATUS_MODAL_WIN_WIDTH = 700;
+
+    /**
+     * Application preferences.
+     */
+    @SpringBean
+    protected PreferenceManager prefMan;
+
+    protected final AbstractSubjectRestClient restClient;
+
+    /**
+     * Number of rows per page.
+     */
+    private final int rows;
+
+    /**
+     * Container used to refresh table.
+     */
+    protected final WebMarkupContainer container;
+
+    /**
+     * Feedback panel specified by the caller.
+     */
+    protected final NotificationPanel feedbackPanel;
+
+    /**
+     * Specify if results are about a filtered search or not. Using this attribute it is possible to use this panel to
+     * show results about user list and user search.
+     */
+    private final boolean filtered;
+
+    /**
+     * Filter used in case of filtered search.
+     */
+    private String fiql;
+
+    /**
+     * Result table.
+     */
+    private AjaxDataTablePanel<AbstractAttributableTO, String> resultTable;
+
+    /**
+     * Data provider used to search for users.
+     */
+    private AttributableDataProvider dataProvider;
+
+    /**
+     * Modal window to be used for user profile editing. Global visibility is required ...
+     */
+    protected final ModalWindow editmodal = new ModalWindow("editModal");
+
+    /**
+     * Modal window to be used for attributes choosing to display in tables.
+     */
+    protected final ModalWindow displaymodal = new ModalWindow("displayModal");
+
+    /**
+     * Modal window to be used for user status management.
+     */
+    protected final ModalWindow statusmodal = new ModalWindow("statusModal");
+
+    /**
+     * Owner page.
+     */
+    protected final AbstractBasePage page;
+
+    protected <T extends AbstractAttributableTO> AbstractSearchResultPanel(final String id, final boolean filtered,
+            final String fiql, final PageReference pageRef, final AbstractSubjectRestClient restClient) {
+
+        super(id);
+
+        setOutputMarkupId(true);
+
+        this.page = (AbstractBasePage) pageRef.getPage();
+
+        this.filtered = filtered;
+        this.fiql = fiql;
+        this.feedbackPanel = page.getFeedbackPanel();
+
+        this.restClient = restClient;
+
+        editmodal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        editmodal.setInitialHeight(EDIT_MODAL_WIN_HEIGHT);
+        editmodal.setInitialWidth(EDIT_MODAL_WIN_WIDTH);
+        editmodal.setCookieName("edit-modal");
+        add(editmodal);
+
+        displaymodal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        displaymodal.setInitialHeight(DISPLAYATTRS_MODAL_WIN_HEIGHT);
+        displaymodal.setInitialWidth(DISPLAYATTRS_MODAL_WIN_WIDTH);
+        displaymodal.setCookieName("display-modal");
+        add(displaymodal);
+
+        statusmodal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        statusmodal.setInitialHeight(STATUS_MODAL_WIN_HEIGHT);
+        statusmodal.setInitialWidth(STATUS_MODAL_WIN_WIDTH);
+        statusmodal.setCookieName("status-modal");
+        add(statusmodal);
+
+        // Container for user search result
+        container = new WebMarkupContainer("container");
+        container.setOutputMarkupId(true);
+        add(container);
+
+        rows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_USERS_PAGINATOR_ROWS);
+    }
+
+    protected void initResultTable() {
+        // ---------------------------
+        // Result table initialization
+        // ---------------------------
+        updateResultTable(false);
+        // ---------------------------
+
+        // ---------------------------
+        // Rows-per-page selector
+        // ---------------------------
+        final Form<?> paginatorForm = new Form<>("paginator");
+        container.add(paginatorForm);
+
+        final DropDownChoice<Integer> rowsChooser = new DropDownChoice<>(
+                "rowsChooser", new PropertyModel<Integer>(this, "rows"), prefMan.getPaginatorChoices());
+
+        rowsChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                prefMan.set(getRequest(), getResponse(), Constants.PREF_USERS_PAGINATOR_ROWS, String.valueOf(rows));
+
+                final EventDataWrapper data = new EventDataWrapper();
+                data.setTarget(target);
+                data.setRows(rows);
+
+                send(getParent(), Broadcast.BREADTH, data);
+            }
+        });
+        paginatorForm.add(rowsChooser);
+        // ---------------------------
+
+        setWindowClosedReloadCallback(statusmodal);
+        setWindowClosedReloadCallback(editmodal);
+        setWindowClosedReloadCallback(displaymodal);
+    }
+
+    public void search(final String fiql, final AjaxRequestTarget target) {
+        this.fiql = fiql;
+        dataProvider.setFIQL(fiql);
+        target.add(container);
+    }
+
+    private void updateResultTable(final boolean create) {
+        updateResultTable(create, rows);
+    }
+
+    private void updateResultTable(final boolean create, final int rows) {
+        dataProvider = new AttributableDataProvider(restClient, rows, filtered);
+        dataProvider.setFIQL(fiql);
+
+        final int currentPage = resultTable != null
+                ? (create
+                        ? (int) resultTable.getPageCount() - 1
+                        : (int) resultTable.getCurrentPage())
+                : 0;
+
+        resultTable = new AjaxDataTablePanel<>(
+                "resultTable",
+                getColumns(),
+                dataProvider,
+                rows,
+                getBulkActions(),
+                restClient,
+                "key",
+                getPageId(),
+                page.getPageReference());
+
+        resultTable.setCurrentPage(currentPage);
+
+        resultTable.setOutputMarkupId(true);
+
+        container.addOrReplace(resultTable);
+    }
+
+    protected abstract List<IColumn<AbstractAttributableTO, String>> getColumns();
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof EventDataWrapper) {
+            final EventDataWrapper data = (EventDataWrapper) event.getPayload();
+
+            if (data.getRows() < 1) {
+                updateResultTable(data.isCreate());
+            } else {
+                updateResultTable(data.isCreate(), data.getRows());
+            }
+
+            data.getTarget().add(container);
+        }
+    }
+
+    private void setWindowClosedReloadCallback(final ModalWindow window) {
+        window.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487139L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                final EventDataWrapper data = new EventDataWrapper();
+                data.setTarget(target);
+                data.setRows(rows);
+
+                send(getParent(), Broadcast.BREADTH, data);
+
+                if (page.isModalResult()) {
+                    // reset modal result
+                    page.setModalResult(false);
+                    // set operation succeeded
+                    getSession().info(getString(Constants.OPERATION_SUCCEEDED));
+                    // refresh feedback panel
+                    feedbackPanel.refresh(target);
+                }
+            }
+        });
+    }
+
+    public static class EventDataWrapper {
+
+        private AjaxRequestTarget target;
+
+        private boolean create;
+
+        private int rows;
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public void setTarget(final AjaxRequestTarget target) {
+            this.target = target;
+        }
+
+        public boolean isCreate() {
+            return create;
+        }
+
+        public void setCreate(final boolean create) {
+            this.create = create;
+        }
+
+        public int getRows() {
+            return rows;
+        }
+
+        public void setRows(final int rows) {
+            this.rows = rows;
+        }
+    }
+
+    protected abstract <T extends AbstractAttributableTO> Collection<ActionLink.ActionType> getBulkActions();
+
+    protected abstract String getPageId();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/AccountPolicy.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AccountPolicy.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AccountPolicy.java
new file mode 100644
index 0000000..60aae41
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AccountPolicy.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AccountPolicy extends Panel {
+
+    private static final long serialVersionUID = -1100228004207271270L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(AccountPolicy.class);
+
+    public AccountPolicy(final String id) {
+        super(id);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/ActionDataTablePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ActionDataTablePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ActionDataTablePanel.java
new file mode 100644
index 0000000..80d05a1
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ActionDataTablePanel.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.client.console.commons.ActionTableCheckGroup;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxButton;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CheckGroupColumn;
+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.wicket.AttributeModifier;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+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.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public class ActionDataTablePanel<T, S> extends DataTablePanel<T, S> {
+
+    private static final long serialVersionUID = -8826989026203543957L;
+
+    private static final String CANCEL = "cancel";
+
+    private final Form<T> bulkActionForm;
+
+    private final ActionLinksPanel actionPanel;
+
+    private final PageReference pageRef;
+
+    public ActionDataTablePanel(
+            final String id,
+            final List<IColumn<T, S>> columns,
+            final ISortableDataProvider<T, S> dataProvider,
+            final int rowsPerPage,
+            final PageReference pageRef) {
+
+        super(id);
+
+        this.pageRef = pageRef;
+
+        bulkActionForm = new Form<>("groupForm");
+        add(bulkActionForm);
+
+        group = new ActionTableCheckGroup<T>("checkgroup", model) {
+
+            private static final long serialVersionUID = -8667764190925075389L;
+
+            @Override
+            public boolean isCheckable(final T element) {
+                return isElementEnabled(element);
+            }
+        };
+        group.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                // triggers AJAX form submit
+            }
+        });
+        bulkActionForm.add(group);
+
+        columns.add(0, new CheckGroupColumn<T, S>(group));
+        dataTable = new AjaxFallbackDefaultDataTable<>("dataTable", columns, dataProvider, rowsPerPage);
+        group.add(dataTable);
+
+        final WebMarkupContainer actionPanelContainer = new WebMarkupContainer("actionPanelContainer");
+        bulkActionForm.add(actionPanelContainer);
+
+        actionPanel = new ActionLinksPanel("actions", new Model(), pageRef);
+        actionPanelContainer.add(actionPanel);
+
+        if (dataTable.getRowCount() == 0) {
+            actionPanelContainer.add(new AttributeModifier("style", "display: none"));
+        }
+
+        bulkActionForm.add(new ClearIndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL), pageRef) {
+
+            private static final long serialVersionUID = -2341391430136818025L;
+
+            @Override
+            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                // ignore
+            }
+        }.setVisible(false).setEnabled(false));
+    }
+
+    public void addAction(final ActionLink action, final ActionType type, final String entitlements) {
+        actionPanel.add(action, type, entitlements);
+    }
+
+    public void addAction(final ActionLink action, final ActionType type, final String pageId, final boolean enabled) {
+        actionPanel.add(action, type, pageId, enabled);
+    }
+
+    public void addCancelButton(final ModalWindow window) {
+
+        final AjaxButton cancel = new ClearIndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL), pageRef) {
+
+            private static final long serialVersionUID = -2341391430136818025L;
+
+            @Override
+            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+        }.feedbackPanelAutomaticReload(false);
+
+        cancel.setDefaultFormProcessing(false);
+        bulkActionForm.addOrReplace(cancel);
+    }
+
+    public Collection<T> getModelObject() {
+        return group.getModelObject();
+    }
+
+    public boolean isElementEnabled(final T element) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
new file mode 100644
index 0000000..563bd17
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AjaxDataTablePanel.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.client.console.rest.BaseRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.panels.AbstractSearchResultPanel.EventDataWrapper;
+import org.apache.syncope.client.console.pages.AbstractBasePage;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CheckGroupColumn;
+import org.apache.syncope.client.console.wicket.ajax.markup.html.ClearIndicatingAjaxButton;
+import org.apache.syncope.client.console.pages.BulkActionModalPage;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.event.Broadcast;
+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.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
+import org.apache.wicket.markup.html.form.CheckGroup;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Fragment;
+
+public class AjaxDataTablePanel<T, S> extends DataTablePanel<T, S> {
+
+    private static final long serialVersionUID = -7264400471578272966L;
+
+    public AjaxDataTablePanel(
+            final String id,
+            final List<IColumn<T, S>> columns,
+            final ISortableDataProvider<T, S> dataProvider,
+            final int rowsPerPage,
+            final Collection<ActionLink.ActionType> actions,
+            final BaseRestClient bulkActionExecutor,
+            final String itemIdField,
+            final String pageId,
+            final PageReference pageRef) {
+
+        super(id);
+
+        final ModalWindow bulkModalWin = new ModalWindow("bulkModal");
+        bulkModalWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        bulkModalWin.setInitialHeight(600);
+        bulkModalWin.setInitialWidth(900);
+        bulkModalWin.setCookieName("bulk-modal");
+        add(bulkModalWin);
+
+        bulkModalWin.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
+
+            private static final long serialVersionUID = 8804221891699487149L;
+
+            @Override
+            public void onClose(final AjaxRequestTarget target) {
+                final EventDataWrapper data = new EventDataWrapper();
+                data.setTarget(target);
+                data.setRows(rowsPerPage);
+
+                send(pageRef.getPage(), Broadcast.BREADTH, data);
+
+                final AbstractBasePage page = (AbstractBasePage) pageRef.getPage();
+
+                if (page.isModalResult()) {
+                    // reset modal result
+                    page.setModalResult(false);
+                    // set operation succeeded
+                    getSession().info(getString(Constants.OPERATION_SUCCEEDED));
+                    // refresh feedback panel
+                    target.add(page.getFeedbackPanel());
+                }
+            }
+        });
+
+        Fragment fragment = new Fragment("tablePanel", "bulkAvailable", this);
+        add(fragment);
+
+        Form<T> bulkActionForm = new Form<>("groupForm");
+        fragment.add(bulkActionForm);
+
+        group = new CheckGroup<>("checkgroup", model);
+        group.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                // triggers AJAX form submit
+            }
+        });
+        bulkActionForm.add(group);
+
+        columns.add(0, new CheckGroupColumn<T, S>(group));
+        dataTable = new AjaxFallbackDefaultDataTable<>("dataTable", columns, dataProvider, rowsPerPage);
+        group.add(dataTable);
+
+        fragment.add(new ClearIndicatingAjaxButton("bulkActionLink", bulkActionForm, pageRef) {
+
+            private static final long serialVersionUID = 382302811235019988L;
+
+            @Override
+            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                bulkModalWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690941L;
+
+                    @Override
+                    public Page createPage() {
+                        return new BulkActionModalPage<>(
+                                bulkModalWin,
+                                group.getModelObject(),
+                                columns,
+                                actions,
+                                bulkActionExecutor,
+                                itemIdField,
+                                pageId);
+                    }
+                });
+
+                bulkModalWin.show(target);
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/AnnotatedBeanPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnnotatedBeanPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnnotatedBeanPanel.java
new file mode 100644
index 0000000..e809263
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnnotatedBeanPanel.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.common.lib.to.AbstractAnnotatedBean;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+
+public class AnnotatedBeanPanel extends Panel {
+
+    private static final long serialVersionUID = 4228064224811390809L;
+
+    public <T extends AbstractAttributableTO> AnnotatedBeanPanel(
+            final String id, final AbstractAnnotatedBean sysInfoTO) {
+
+        super(id);
+
+        // ------------------------
+        // Creation date
+        // ------------------------
+        add(new Label("creationDate", new Model<>(sysInfoTO.getCreationDate() != null
+                ? SyncopeConsoleSession.get().getDateFormat().format(sysInfoTO.getCreationDate()) : "")));
+        // ------------------------
+
+        // ------------------------
+        // Last change date
+        // ------------------------
+        add(new Label("lastChangeDate", new Model<>(sysInfoTO.getLastChangeDate() != null
+                ? SyncopeConsoleSession.get().getDateFormat().format(sysInfoTO.getCreationDate()) : "")));
+        // ------------------------
+
+        // ------------------------
+        // Creator
+        // ------------------------
+        add(new Label("creator", new Model<>(sysInfoTO.getCreator() != null
+                ? sysInfoTO.getCreator() : "")));
+        // ------------------------
+
+        // ------------------------
+        // Last modifier
+        // ------------------------
+        add(new Label("lastModifier", new Model<>(sysInfoTO.getLastModifier() != null
+                ? sysInfoTO.getLastModifier() : "")));
+        // ------------------------
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/Any.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/Any.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/Any.java
new file mode 100644
index 0000000..8d99606
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/Any.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Any extends Panel {
+
+    private static final long serialVersionUID = -1100228004207271270L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Any.class);
+
+    public Any(final String id) {
+        super(id);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/Connectors.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/Connectors.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/Connectors.java
new file mode 100644
index 0000000..18f28d7
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/Connectors.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import static org.apache.wicket.Component.ENABLE;
+import static org.apache.wicket.Component.RENDER;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.syncope.client.console.PreferenceManager;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.pages.ConnectorModalPage;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+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.ActionLinksPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxCallListener;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+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.event.IEvent;
+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.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.ISortableDataProvider;
+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.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.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Resources WebPage.
+ */
+public class Connectors extends Panel {
+
+    private static final long serialVersionUID = -3789252860990261728L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Connectors.class);
+
+    private static final int WIN_HEIGHT = 600;
+
+    private static final int WIN_WIDTH = 1100;
+
+    @SpringBean
+    private ConnectorRestClient connectorRestClient;
+
+    @SpringBean
+    private PreferenceManager prefMan;
+
+    private final ModalWindow createConnectorWin;
+
+    private final ModalWindow editConnectorWin;
+
+    private final int connectorPaginatorRows;
+
+    private WebMarkupContainer connectorContainer;
+
+    /**
+     * Modal window to be used for user status management.
+     */
+    protected final ModalWindow statusmodal = new ModalWindow("statusModal");
+
+    /**
+     * Schemas to be shown modal window height.
+     */
+    private static final int STATUS_MODAL_WIN_HEIGHT = 500;
+
+    /**
+     * Schemas to be shown modal window width.
+     */
+    private static final int STATUS_MODAL_WIN_WIDTH = 700;
+
+    private final PageReference pageRef;
+
+    public Connectors(final String id, final PageReference pageRef) {
+        super(id);
+        this.pageRef = pageRef;
+
+        createConnectorWin = new ModalWindow("createConnectorWin");
+        add(createConnectorWin);
+
+        editConnectorWin = new ModalWindow("editConnectorWin");
+        add(editConnectorWin);
+
+        statusmodal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        statusmodal.setInitialHeight(STATUS_MODAL_WIN_HEIGHT);
+        statusmodal.setInitialWidth(STATUS_MODAL_WIN_WIDTH);
+        statusmodal.setCookieName("status-modal");
+        add(statusmodal);
+
+        AjaxLink<Void> reloadLink = new ClearIndicatingAjaxLink<Void>("reloadLink", pageRef) {
+
+            private static final long serialVersionUID = 3109256773218160485L;
+
+            @Override
+            protected void onClickInternal(final AjaxRequestTarget target) {
+                try {
+                    connectorRestClient.reload();
+                    info(getString(Constants.OPERATION_SUCCEEDED));
+                } catch (Exception e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                }
+                ((BasePage) pageRef.getPage()).getFeedbackPanel().refresh(target);
+                target.add(connectorContainer);
+            }
+
+            @Override
+            protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
+                super.updateAjaxAttributes(attributes);
+
+                final AjaxCallListener ajaxCallListener = new AjaxCallListener() {
+
+                    private static final long serialVersionUID = 7160235486520935153L;
+
+                    @Override
+                    public CharSequence getPrecondition(final Component component) {
+                        return "if (!confirm('" + getString("confirmReloadConnectors") + "')) "
+                                + "{return false;} else {return true;}";
+                    }
+                };
+                attributes.getAjaxCallListeners().add(ajaxCallListener);
+            }
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(reloadLink, ENABLE, Entitlement.CONNECTOR_RELOAD);
+        add(reloadLink);
+
+        connectorPaginatorRows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_CONNECTORS_PAGINATOR_ROWS);
+
+        setupConnectors();
+    }
+
+    private void setupConnectors() {
+        List<IColumn<ConnInstanceTO, String>> columns = new ArrayList<>();
+
+        columns.add(new PropertyColumn<ConnInstanceTO, String>(
+                new StringResourceModel("key", this, null), "key", "key"));
+        columns.add(new PropertyColumn<ConnInstanceTO, String>(
+                new StringResourceModel("name", this, null), "connectorName", "connectorName"));
+        columns.add(new PropertyColumn<ConnInstanceTO, String>(
+                new StringResourceModel("displayName", this, null), "displayName", "displayName"));
+        columns.add(new PropertyColumn<ConnInstanceTO, String>(
+                new StringResourceModel("bundleName", this, null), "bundleName", "bundleName"));
+        columns.add(new PropertyColumn<ConnInstanceTO, String>(
+                new StringResourceModel("version", this, null), "version", "version"));
+        columns.add(new AbstractColumn<ConnInstanceTO, String>(new StringResourceModel("actions", this, null, "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public String getCssClass() {
+                return "action";
+            }
+
+            @Override
+            public void populateItem(final Item<ICellPopulator<ConnInstanceTO>> cellItem, final String componentId,
+                    final IModel<ConnInstanceTO> model) {
+
+                final ConnInstanceTO connectorTO = model.getObject();
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageRef);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        editConnectorWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new ConnectorModalPage(Connectors.this.pageRef,
+                                        editConnectorWin,
+                                        connectorTO);
+                            }
+                        });
+
+                        editConnectorWin.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, "Connectors");
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        try {
+                            connectorRestClient.delete(connectorTO.getKey());
+                            info(getString(Constants.OPERATION_SUCCEEDED));
+                        } catch (SyncopeClientException e) {
+                            error(getString(Constants.ERROR) + ": " + e.getMessage());
+
+                            LOG.error("While deleting connector " + connectorTO.getKey(), e);
+                        }
+
+                        target.add(connectorContainer);
+                        ((BasePage) pageRef.getPage()).getFeedbackPanel().refresh(target);
+                    }
+                }, ActionLink.ActionType.DELETE, "Connectors");
+
+                cellItem.add(panel);
+            }
+        });
+
+        final AjaxDataTablePanel<ConnInstanceTO, String> table = new AjaxDataTablePanel<>(
+                "connectorDatatable",
+                columns,
+                (ISortableDataProvider<ConnInstanceTO, String>) new ConnectorsProvider(),
+                connectorPaginatorRows,
+                Arrays.asList(new ActionLink.ActionType[] { ActionLink.ActionType.DELETE }),
+                connectorRestClient,
+                "key",
+                "Connectors",
+                pageRef);
+
+        connectorContainer = new WebMarkupContainer("connectorContainer");
+        connectorContainer.add(table);
+        connectorContainer.setOutputMarkupId(true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(connectorContainer, RENDER, Entitlement.CONNECTOR_LIST);
+
+        add(connectorContainer);
+
+        ((BasePage) pageRef.getPage()).setWindowClosedCallback(createConnectorWin, connectorContainer);
+        ((BasePage) pageRef.getPage()).setWindowClosedCallback(editConnectorWin, connectorContainer);
+
+        createConnectorWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        createConnectorWin.setInitialHeight(WIN_HEIGHT);
+        createConnectorWin.setInitialWidth(WIN_WIDTH);
+        createConnectorWin.setCookieName("create-conn-modal");
+
+        editConnectorWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        editConnectorWin.setInitialHeight(WIN_HEIGHT);
+        editConnectorWin.setInitialWidth(WIN_WIDTH);
+        editConnectorWin.setCookieName("edit-conn-modal");
+
+        AjaxLink<Void> createConnectorLink
+                = new ClearIndicatingAjaxLink<Void>("createConnectorLink", pageRef) {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    protected void onClickInternal(final AjaxRequestTarget target) {
+                        createConnectorWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                ConnectorModalPage form = new ConnectorModalPage(
+                                        pageRef, editConnectorWin, new ConnInstanceTO());
+                                return form;
+                            }
+                        });
+
+                        createConnectorWin.show(target);
+                    }
+                };
+
+        MetaDataRoleAuthorizationStrategy.authorize(createConnectorLink, ENABLE, Entitlement.CONNECTOR_CREATE);
+
+        add(createConnectorLink);
+
+        @SuppressWarnings("rawtypes")
+        Form paginatorForm = new Form("connectorPaginatorForm");
+
+        MetaDataRoleAuthorizationStrategy.authorize(paginatorForm, RENDER, Entitlement.CONNECTOR_LIST);
+
+        final DropDownChoice<Integer> rowsChooser = new DropDownChoice<>(
+                "rowsChooser",
+                new PropertyModel<Integer>(this,
+                        "connectorPaginatorRows"),
+                prefMan.getPaginatorChoices());
+
+        rowsChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                prefMan.set(getRequest(), getResponse(), Constants.PREF_CONNECTORS_PAGINATOR_ROWS,
+                        String.valueOf(connectorPaginatorRows));
+                table.setItemsPerPage(connectorPaginatorRows);
+
+                target.add(connectorContainer);
+            }
+        });
+
+        paginatorForm.add(rowsChooser);
+        add(paginatorForm);
+    }
+
+    private class ConnectorsProvider extends SortableDataProvider<ConnInstanceTO, String> {
+
+        private static final long serialVersionUID = 4445909568349448518L;
+
+        private final SortableDataProviderComparator<ConnInstanceTO> comparator;
+
+        public ConnectorsProvider() {
+            super();
+            //Default sorting
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<ConnInstanceTO> iterator(final long first, final long count) {
+            List<ConnInstanceTO> list = connectorRestClient.getAllConnectors();
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return connectorRestClient.getAllConnectors().size();
+        }
+
+        @Override
+        public IModel<ConnInstanceTO> model(final ConnInstanceTO connector) {
+
+            return new AbstractReadOnlyModel<ConnInstanceTO>() {
+
+                private static final long serialVersionUID = -6033068018293569398L;
+
+                @Override
+                public ConnInstanceTO getObject() {
+                    return connector;
+                }
+            };
+        }
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof AbstractSearchResultPanel.EventDataWrapper) {
+            ((AbstractSearchResultPanel.EventDataWrapper) event.getPayload()).getTarget().add(connectorContainer);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/DataTablePanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/DataTablePanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/DataTablePanel.java
new file mode 100644
index 0000000..1800d54
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/DataTablePanel.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import org.apache.wicket.Component;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.DataGridView;
+import org.apache.wicket.markup.html.form.CheckGroup;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.IModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class DataTablePanel<T, S> extends Panel {
+
+    private static final long serialVersionUID = -7264400471578272966L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(DataTablePanel.class);
+
+    protected CheckGroup<T> group;
+
+    protected AjaxFallbackDefaultDataTable<T, S> dataTable;
+
+    protected IModel<Collection<T>> model;
+
+    public DataTablePanel(final String id) {
+        super(id);
+
+        model = new IModel<Collection<T>>() {
+
+            private static final long serialVersionUID = 4886729136344643465L;
+
+            private final Collection<T> values = new HashSet<>();
+
+            @Override
+            public Collection<T> getObject() {
+                // Someone or something call this method to change the model: this is not the right behavior.
+                // Return a copy of the model object in order to avoid SYNCOPE-465
+                return new HashSet<>(values);
+            }
+
+            @Override
+            public void setObject(final Collection<T> selected) {
+                final Collection<T> all = getGroupModelObjects();
+                values.removeAll(all);
+                values.addAll(selected);
+            }
+
+            @Override
+            public void detach() {
+            }
+        };
+    }
+
+    public final void setCurrentPage(final long page) {
+        dataTable.setCurrentPage(page);
+    }
+
+    public final long getRowCount() {
+        return dataTable.getRowCount();
+    }
+
+    public final long getCurrentPage() {
+        return dataTable.getCurrentPage();
+    }
+
+    public final long getPageCount() {
+        return dataTable.getPageCount();
+    }
+
+    public void setItemsPerPage(final int resourcePaginatorRows) {
+        dataTable.setItemsPerPage(resourcePaginatorRows);
+    }
+
+    protected Collection<T> getGroupModelObjects() {
+        final Set<T> res = new HashSet<>();
+
+        final Component rows = group.get("dataTable:body:rows");
+        if (rows instanceof DataGridView) {
+            @SuppressWarnings("unchecked")
+            final Iterator<Item<T>> iter = ((DataGridView<T>) rows).getItems();
+
+            while (iter.hasNext()) {
+                res.add(iter.next().getModelObject());
+            }
+        }
+        return res;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/PasswordPolicy.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PasswordPolicy.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PasswordPolicy.java
new file mode 100644
index 0000000..8bad8ff
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PasswordPolicy.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PasswordPolicy extends Panel {
+
+    private static final long serialVersionUID = -1100228004207271270L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(PasswordPolicy.class);
+
+    public PasswordPolicy(final String id) {
+        super(id);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
new file mode 100644
index 0000000..f31c87c
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Realm extends Panel {
+
+    private static final long serialVersionUID = -1100228004207271270L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(Realm.class);
+
+    private final RealmTO realmTO;
+
+    public Realm(final String id, final RealmTO realmTO) {
+        super(id);
+        this.realmTO = realmTO;
+
+        add(new RealmDetails("details", realmTO));
+        add(new Any("users"));
+        add(new Any("groups"));
+        add(new Any("services"));
+        add(new Any("serviceRoles"));
+        add(new Any("contexts"));
+        add(new Any("enactmentEngine"));
+        add(new AccountPolicy("accountPolicy"));
+        add(new PasswordPolicy("passwordPolicy"));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.java
new file mode 100644
index 0000000..391dba2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/RealmDetails.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.panels;
+
+import org.apache.syncope.common.lib.to.RealmTO;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.PropertyModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RealmDetails extends Panel {
+
+    private static final long serialVersionUID = -1100228004207271270L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(RealmDetails.class);
+
+    public RealmDetails(final String id, final RealmTO realmTO) {
+        super(id);
+        add(new TextField<>("id", new PropertyModel<>(realmTO, "key")).setEnabled(false));
+        add(new TextField<>("name", new PropertyModel<>(realmTO, "name")).setEnabled(false));
+        add(new TextField<>("path", new PropertyModel<>(realmTO, "fullPath")).setEnabled(false));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java
new file mode 100644
index 0000000..c1d01e0
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceConnConfPanel.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.client.console.pages.BaseModalPage;
+import org.apache.syncope.client.console.pages.ResourceModalPage.ResourceEvent;
+import org.apache.syncope.client.console.panels.ResourceDetailsPanel.DetailsModEvent;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel.MultiValueSelectorEvent;
+import org.apache.syncope.client.console.wicket.markup.html.list.ConnConfPropertyListView;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class ResourceConnConfPanel extends Panel {
+
+    private static final long serialVersionUID = -7982691107029848579L;
+
+    @SpringBean
+    private ConnectorRestClient restClient;
+
+    private final ResourceTO resourceTO;
+
+    private final boolean createFlag;
+
+    private List<ConnConfProperty> connConfProperties;
+
+    private WebMarkupContainer connConfPropContainer;
+
+    private AjaxButton check;
+
+    public ResourceConnConfPanel(final String id, final ResourceTO resourceTO, final boolean createFlag) {
+        super(id);
+        setOutputMarkupId(true);
+
+        this.createFlag = createFlag;
+        this.resourceTO = resourceTO;
+
+        connConfProperties = getConnConfProperties();
+
+        connConfPropContainer = new WebMarkupContainer("connectorPropertiesContainer");
+        connConfPropContainer.setOutputMarkupId(true);
+        add(connConfPropContainer);
+
+        /*
+         * the list of overridable connector properties
+         */
+        final ListView<ConnConfProperty> connPropView = new ConnConfPropertyListView("connectorProperties",
+                new PropertyModel<List<ConnConfProperty>>(this, "connConfProperties"),
+                false, resourceTO.getConnConfProperties());
+        connPropView.setOutputMarkupId(true);
+        connConfPropContainer.add(connPropView);
+
+        check = new IndicatingAjaxButton("check", new ResourceModel("check")) {
+
+            private static final long serialVersionUID = -4199438518229098169L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                final ResourceTO to = (ResourceTO) form.getModelObject();
+
+                if (restClient.check(to)) {
+                    info(getString("success_connection"));
+                } else {
+                    error(getString("error_connection"));
+                }
+
+                ((BaseModalPage) getPage()).getFeedbackPanel().refresh(target);
+            }
+        };
+
+        check.setEnabled(!connConfProperties.isEmpty());
+        check.setVisible(!connConfProperties.isEmpty());
+        
+        connConfPropContainer.add(check);
+    }
+
+    /**
+     * Get overridable properties.
+     *
+     * @return overridable properties.
+     */
+    private List<ConnConfProperty> getConnConfProperties() {
+        final List<ConnConfProperty> props = new ArrayList<ConnConfProperty>();
+        final Long connectorId = resourceTO.getConnectorId();
+        if (connectorId != null && connectorId > 0) {
+            for (ConnConfProperty property : restClient.getConnectorProperties(connectorId)) {
+                if (property.isOverridable()) {
+                    props.add(property);
+                }
+            }
+        }
+        if (createFlag || resourceTO.getConnConfProperties().isEmpty()) {
+            resourceTO.getConnConfProperties().clear();
+        } else {
+            Map<String, ConnConfProperty> valuedProps = new HashMap<String, ConnConfProperty>();
+            for (ConnConfProperty prop : resourceTO.getConnConfProperties()) {
+                valuedProps.put(prop.getSchema().getName(), prop);
+            }
+
+            for (int i = 0; i < props.size(); i++) {
+                if (valuedProps.containsKey(props.get(i).getSchema().getName())) {
+                    props.set(i, valuedProps.get(props.get(i).getSchema().getName()));
+                }
+            }
+        }
+
+        // re-order properties
+        Collections.sort(props);
+
+        return props;
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        AjaxRequestTarget target = null;
+        if (event.getPayload() instanceof DetailsModEvent) {
+            // connector change: update properties and forward event
+            target = ((ResourceEvent) event.getPayload()).getTarget();
+
+            connConfProperties = getConnConfProperties();
+            check.setEnabled(!connConfProperties.isEmpty());
+
+            target.add(connConfPropContainer);
+        } else if (event.getPayload() instanceof MultiValueSelectorEvent) {
+            // multi value connector property change: forward event
+            target = ((MultiValueSelectorEvent) event.getPayload()).getTarget();
+        }
+
+        if (target != null) {
+            send(getPage(), Broadcast.BREADTH, new ConnConfModEvent(target, connConfProperties));
+        }
+    }
+
+    /**
+     * Connector configuration properties modification event.
+     */
+    public static class ConnConfModEvent extends ResourceEvent {
+
+        private List<ConnConfProperty> configuration;
+
+        /**
+         * Constructor.
+         *
+         * @param target request target.
+         * @param configuration connector configuration properties.
+         */
+        public ConnConfModEvent(final AjaxRequestTarget target, final List<ConnConfProperty> configuration) {
+            super(target);
+            this.configuration = configuration;
+        }
+
+        public List<ConnConfProperty> getConfiguration() {
+            return configuration;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34ec6712/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java
new file mode 100644
index 0000000..3bff23f
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDetailsPanel.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.ResourceModalPage.ResourceEvent;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.PropagationMode;
+import org.apache.syncope.common.lib.types.TraceLevel;
+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.event.Broadcast;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.ChoiceRenderer;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+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.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ResourceDetailsPanel extends Panel {
+
+    private static final long serialVersionUID = -7982691107029848579L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(ResourceDetailsPanel.class);
+
+    @SpringBean
+    private ConnectorRestClient connRestClient;
+
+    private ConnInstanceTO connInstanceTO;
+
+    public ResourceDetailsPanel(final String id, final ResourceTO resourceTO, final List<String> actionClassNames,
+            final boolean createFlag) {
+
+        super(id);
+        setOutputMarkupId(true);
+
+        final AjaxTextFieldPanel resourceName = new AjaxTextFieldPanel("name", new ResourceModel("name", "name").
+                getObject(), new PropertyModel<String>(resourceTO, "key"));
+
+        resourceName.setEnabled(createFlag);
+        resourceName.addRequiredLabel();
+        add(resourceName);
+
+        final AjaxCheckBoxPanel enforceMandatoryCondition = new AjaxCheckBoxPanel("enforceMandatoryCondition",
+                new ResourceModel("enforceMandatoryCondition", "enforceMandatoryCondition").getObject(),
+                new PropertyModel<Boolean>(resourceTO, "enforceMandatoryCondition"));
+        add(enforceMandatoryCondition);
+
+        final AjaxCheckBoxPanel propagationPrimary = new AjaxCheckBoxPanel("propagationPrimary", new ResourceModel(
+                "propagationPrimary", "propagationPrimary").getObject(), new PropertyModel<Boolean>(resourceTO,
+                        "propagationPrimary"));
+        add(propagationPrimary);
+
+        final SpinnerFieldPanel<Integer> propagationPriority =
+                new SpinnerFieldPanel<>("propagationPriority", "propagationPriority", Integer.class,
+                        new PropertyModel<Integer>(resourceTO, "propagationPriority"), null, null);
+        add(propagationPriority);
+
+        final AjaxDropDownChoicePanel<PropagationMode> propagationMode = new AjaxDropDownChoicePanel<>(
+                "propagationMode", new ResourceModel("propagationMode", "propagationMode").getObject(),
+                new PropertyModel<PropagationMode>(resourceTO, "propagationMode"));
+        propagationMode.setChoices(Arrays.asList(PropagationMode.values()));
+        add(propagationMode);
+
+        final AjaxCheckBoxPanel randomPwdIfNotProvided = new AjaxCheckBoxPanel("randomPwdIfNotProvided",
+                new ResourceModel("randomPwdIfNotProvided", "randomPwdIfNotProvided").getObject(),
+                new PropertyModel<Boolean>(resourceTO, "randomPwdIfNotProvided"));
+        add(randomPwdIfNotProvided);
+
+        final WebMarkupContainer propagationActionsClassNames = new WebMarkupContainer("propagationActionsClassNames");
+        propagationActionsClassNames.setOutputMarkupId(true);
+        add(propagationActionsClassNames);
+
+        final AjaxLink<Void> first = new IndicatingAjaxLink<Void>("first") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                resourceTO.getPropagationActionsClassNames().add(StringUtils.EMPTY);
+                setVisible(false);
+                target.add(propagationActionsClassNames);
+            }
+        };
+        first.setOutputMarkupPlaceholderTag(true);
+        first.setVisible(resourceTO.getPropagationActionsClassNames().isEmpty());
+        propagationActionsClassNames.add(first);
+
+        final ListView<String> actionsClasses = new ListView<String>("actionsClasses",
+                new PropertyModel<List<String>>(resourceTO, "propagationActionsClassNames")) {
+
+                    private static final long serialVersionUID = 9101744072914090143L;
+
+                    @Override
+                    protected void populateItem(final ListItem<String> item) {
+                        final String className = item.getModelObject();
+
+                        final DropDownChoice<String> actionsClass = new DropDownChoice<>(
+                                "actionsClass", new Model<>(className), actionClassNames);
+                        actionsClass.setNullValid(true);
+                        actionsClass.setRequired(true);
+                        actionsClass.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
+
+                            private static final long serialVersionUID = -1107858522700306810L;
+
+                            @Override
+                            protected void onUpdate(final AjaxRequestTarget target) {
+                                resourceTO.getPropagationActionsClassNames().
+                                set(item.getIndex(), actionsClass.getModelObject());
+                            }
+                        });
+                        actionsClass.setRequired(true);
+                        actionsClass.setOutputMarkupId(true);
+                        actionsClass.setRequired(true);
+                        item.add(actionsClass);
+
+                        AjaxLink<Void> minus = new IndicatingAjaxLink<Void>("drop") {
+
+                            private static final long serialVersionUID = -7978723352517770644L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                resourceTO.getPropagationActionsClassNames().remove(className);
+                                first.setVisible(resourceTO.getPropagationActionsClassNames().isEmpty());
+                                target.add(propagationActionsClassNames);
+                            }
+                        };
+                        item.add(minus);
+
+                        final AjaxLink<Void> plus = new IndicatingAjaxLink<Void>("add") {
+
+                            private static final long serialVersionUID = -7978723352517770644L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                resourceTO.getPropagationActionsClassNames().add(StringUtils.EMPTY);
+                                target.add(propagationActionsClassNames);
+                            }
+                        };
+                        plus.setOutputMarkupPlaceholderTag(true);
+                        plus.setVisible(item.getIndex() == resourceTO.getPropagationActionsClassNames().size() - 1);
+                        item.add(plus);
+                    }
+                };
+        propagationActionsClassNames.add(actionsClasses);
+
+        final AjaxDropDownChoicePanel<TraceLevel> createTraceLevel = new AjaxDropDownChoicePanel<>(
+                "createTraceLevel", new ResourceModel("createTraceLevel", "createTraceLevel").getObject(),
+                new PropertyModel<TraceLevel>(resourceTO, "createTraceLevel"));
+        createTraceLevel.setChoices(Arrays.asList(TraceLevel.values()));
+        add(createTraceLevel);
+
+        final AjaxDropDownChoicePanel<TraceLevel> updateTraceLevel = new AjaxDropDownChoicePanel<>(
+                "updateTraceLevel", new ResourceModel("updateTraceLevel", "updateTraceLevel").getObject(),
+                new PropertyModel<TraceLevel>(resourceTO, "updateTraceLevel"));
+        updateTraceLevel.setChoices(Arrays.asList(TraceLevel.values()));
+        add(updateTraceLevel);
+
+        final AjaxDropDownChoicePanel<TraceLevel> deleteTraceLevel = new AjaxDropDownChoicePanel<>(
+                "deleteTraceLevel", new ResourceModel("deleteTraceLevel", "deleteTraceLevel").getObject(),
+                new PropertyModel<TraceLevel>(resourceTO, "deleteTraceLevel"));
+        deleteTraceLevel.setChoices(Arrays.asList(TraceLevel.values()));
+        add(deleteTraceLevel);
+
+        final AjaxDropDownChoicePanel<TraceLevel> syncTraceLevel = new AjaxDropDownChoicePanel<>(
+                "syncTraceLevel", new ResourceModel("syncTraceLevel", "syncTraceLevel").getObject(),
+                new PropertyModel<TraceLevel>(resourceTO, "syncTraceLevel"));
+        syncTraceLevel.setChoices(Arrays.asList(TraceLevel.values()));
+        add(syncTraceLevel);
+
+        final IModel<List<ConnInstanceTO>> connectors = new LoadableDetachableModel<List<ConnInstanceTO>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<ConnInstanceTO> load() {
+                return connRestClient.getAllConnectors();
+            }
+        };
+
+        connInstanceTO = getConectorInstanceTO(connectors.getObject(), resourceTO);
+
+        final AjaxDropDownChoicePanel<ConnInstanceTO> conn = new AjaxDropDownChoicePanel<>("connector",
+                new ResourceModel("connector", "connector").getObject(),
+                new PropertyModel<ConnInstanceTO>(this, "connInstanceTO"));
+        conn.setChoices(connectors.getObject());
+        conn.setChoiceRenderer(new ChoiceRenderer("displayName", "key"));
+
+        conn.getField().setModel(new IModel<ConnInstanceTO>() {
+
+            private static final long serialVersionUID = -4202872830392400310L;
+
+            @Override
+            public ConnInstanceTO getObject() {
+                return connInstanceTO;
+            }
+
+            @Override
+            public void setObject(final ConnInstanceTO connector) {
+                resourceTO.setConnectorId(connector.getKey());
+                connInstanceTO = connector;
+            }
+
+            @Override
+            public void detach() {
+            }
+        });
+
+        conn.addRequiredLabel();
+        conn.setEnabled(createFlag);
+
+        conn.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                send(getPage(), Broadcast.BREADTH, new DetailsModEvent(target));
+            }
+        });
+
+        add(conn);
+    }
+
+    /**
+     * Get the connetorTO linked to the resource.
+     *
+     * @param connectorTOs list of all connectors.
+     * @param resourceTO resource.
+     * @return selected connector instance: in case of no connectors available, null; in case of new resource
+     * specification, the first on connector available
+     */
+    private ConnInstanceTO getConectorInstanceTO(final List<ConnInstanceTO> connectorTOs, final ResourceTO resourceTO) {
+        if (connectorTOs.isEmpty()) {
+            resourceTO.setConnectorId(null);
+            return null;
+        } else {
+            // use the first element as default
+            ConnInstanceTO res = connectorTOs.get(0);
+
+            for (ConnInstanceTO to : connectorTOs) {
+                if (Long.valueOf(to.getKey()).equals(resourceTO.getConnectorId())) {
+                    res = to;
+                }
+            }
+
+            // in case of no match
+            resourceTO.setConnectorId(res.getKey());
+
+            return res;
+        }
+    }
+
+    /**
+     * Connector instance modification event.
+     */
+    public static class DetailsModEvent extends ResourceEvent {
+
+        /**
+         * Constructor.
+         *
+         * @param target request target.
+         */
+        public DetailsModEvent(final AjaxRequestTarget target) {
+            super(target);
+        }
+    }
+}