You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/08/12 16:07:18 UTC

[3/4] syncope git commit: [SYNCOPE-156] providing ListView table view + Provision wizard functionalities

[SYNCOPE-156] providing ListView table view  + Provision wizard functionalities


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

Branch: refs/heads/SYNCOPE-156
Commit: ba7f1a5ce9f852e9783853b7397a64a66ac4826c
Parents: 6ad532a
Author: fmartelli <fa...@gmail.com>
Authored: Wed Aug 12 15:55:08 2015 +0200
Committer: fmartelli <fa...@gmail.com>
Committed: Wed Aug 12 15:55:08 2015 +0200

----------------------------------------------------------------------
 .../console/pages/BulkActionModalPage.java      |   9 +-
 .../console/pages/ProvisioningModalPage.java    |  13 +-
 .../console/panels/AbstractResourceModal.java   |  78 +++++
 .../console/panels/ActionDataTablePanel.java    |  14 +-
 .../console/panels/BeanReflectionModal.java     |  36 ++
 .../console/panels/BeanReflectionPanel.java     | 339 +++++++++++++++++++
 .../client/console/panels/ConnectorModal.java   |  50 +--
 .../client/console/panels/ListViewPanel.java    | 322 ++++++++++++++++++
 .../client/console/panels/ModalContent.java     |   8 +-
 .../console/panels/ResourceMappingPanel.java    |  66 ++--
 .../client/console/panels/ResourceModal.java    |  94 +++--
 .../client/console/topology/Topology.java       | 337 ++++++++++--------
 .../client/console/topology/TopologyNode.java   |   5 +-
 .../console/topology/TopologyNodePanel.java     |  47 ++-
 .../markup/html/form/AbstractFieldPanel.java    |   2 +-
 .../wicket/markup/html/form/ActionLink.java     |  23 +-
 .../markup/html/form/ActionLinksPanel.java      | 261 +++++++++++---
 .../form/CheckBoxMultipleChoiceFieldPanel.java  |  13 +-
 .../client/console/wizards/AjaxWizard.java      | 125 +++++++
 .../console/wizards/AjaxWizardButton.java       |  66 ++++
 .../console/wizards/AjaxWizardButtonBar.java    | 111 ++++++
 .../client/console/wizards/ProvisionWizard.java | 123 +++++++
 .../META-INF/resources/css/syncopeConsole.css   | 139 ++++----
 .../resources/META-INF/resources/js/topology.js |  90 ++---
 .../console/panels/BeanReflectionModal.html     |  30 ++
 .../console/panels/BeanReflectionPanel.html     |  39 +++
 .../client/console/panels/ConnectorModal.html   |  41 +--
 .../client/console/panels/ListViewPanel.html    |  60 ++++
 .../client/console/panels/ResourceModal.html    |  14 +-
 .../console/panels/ResourceModal.properties     |   7 +
 .../console/panels/ResourceModal_it.properties  |   7 +
 .../panels/ResourceModal_pt_BR.properties       |   7 +
 .../client/console/topology/Topology.html       |  21 +-
 .../console/topology/TopologyNodePanel.html     |  59 ++--
 .../markup/html/form/ActionLinksPanel.html      |  32 +-
 .../form/CheckBoxMultipleChoiceFieldPanel.html  |   8 +-
 .../wizards/ProvisionWizard$AccountLink.html    |  23 ++
 .../wizards/ProvisionWizard$Mapping.html        |  23 ++
 .../wizards/ProvisionWizard$ObjectType.html     |  30 ++
 .../console/wizards/ProvisionWizard.properties  |  24 ++
 .../wizards/ProvisionWizard_it.properties       |  24 ++
 .../wizards/ProvisionWizard_pt_BR.properties    |  24 ++
 pom.xml                                         |   5 +-
 43 files changed, 2350 insertions(+), 499 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java
index 5567094..6172639 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BulkActionModalPage.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.console.pages;
 
+import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -39,7 +40,6 @@ import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvid
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
 import org.springframework.beans.BeanUtils;
 
@@ -85,7 +85,8 @@ public class BulkActionModalPage<T, S> extends BaseModalPage {
                 Integer.MAX_VALUE).setVisible(items != null && !items.isEmpty()));
 
         @SuppressWarnings("rawtypes")
-        final ActionLinksPanel actionPanel = new ActionLinksPanel("actions", new Model(), getPageReference());
+        final ActionLinksPanel<Serializable> actionPanel
+                = ActionLinksPanel.builder(getPageReference()).build("actions");
         add(actionPanel);
 
         for (ActionLink.ActionType action : actions) {
@@ -118,12 +119,12 @@ public class BulkActionModalPage<T, S> extends BaseModalPage {
                     LOG.error("Bulk action type not supported");
             }
 
-            actionPanel.add(new ActionLink() {
+            actionPanel.add(new ActionLink<Serializable>() {
 
                 private static final long serialVersionUID = -3722207913631435501L;
 
                 @Override
-                public void onClick(final AjaxRequestTarget target) {
+                public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
                     try {
                         final BulkActionResult res = (BulkActionResult) bulkActionExecutor.getClass().
                                 getMethod("bulkAction", BulkAction.class).invoke(bulkActionExecutor, bulkAction);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/pages/ProvisioningModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ProvisioningModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ProvisioningModalPage.java
index 9937e36..32a4921 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/ProvisioningModalPage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ProvisioningModalPage.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.console.pages;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -129,12 +130,12 @@ public class ProvisioningModalPage<T extends AnyTO> extends AbstractStatusModalP
 
         final String pageId = "Resources";
 
-        table.addAction(new ActionLink() {
+        table.addAction(new ActionLink<Serializable>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target) {
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
                 try {
                     bulkAssociationAction(target, ResourceDeassociationActionType.UNLINK, table, columns);
                 } catch (Exception e) {
@@ -145,12 +146,12 @@ public class ProvisioningModalPage<T extends AnyTO> extends AbstractStatusModalP
             }
         }, ActionLink.ActionType.UNLINK, pageId);
 
-        table.addAction(new ActionLink() {
+        table.addAction(new ActionLink<Serializable>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target) {
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
                 try {
                     bulkAssociationAction(target, ResourceDeassociationActionType.DEPROVISION, table, columns);
                 } catch (Exception e) {
@@ -161,12 +162,12 @@ public class ProvisioningModalPage<T extends AnyTO> extends AbstractStatusModalP
             }
         }, ActionLink.ActionType.DEPROVISION, pageId);
 
-        table.addAction(new ActionLink() {
+        table.addAction(new ActionLink<Serializable>() {
 
             private static final long serialVersionUID = -3722207913631435501L;
 
             @Override
-            public void onClick(final AjaxRequestTarget target) {
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
                 try {
                     bulkAssociationAction(target, ResourceDeassociationActionType.UNASSIGN, table, columns);
                 } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractResourceModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractResourceModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractResourceModal.java
new file mode 100644
index 0000000..1ed9050
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AbstractResourceModal.java
@@ -0,0 +1,78 @@
+/*
+ * 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.io.Serializable;
+import org.apache.syncope.client.console.topology.TopologyNode;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+
+/**
+ * Modal window with Resource form.
+ */
+public abstract class AbstractResourceModal extends ModalContent {
+
+    private static final long serialVersionUID = 1734415311027284221L;
+
+    public AbstractResourceModal(final ModalWindow window, final PageReference pageRef) {
+        super(window, pageRef);
+    }
+
+    public static class CreateEvent extends ModalEvent {
+
+        private final Serializable key;
+
+        private final String displayName;
+
+        private final Serializable parent;
+
+        private final TopologyNode.Kind kind;
+
+        public CreateEvent(
+                final Serializable key,
+                final String displayName,
+                final TopologyNode.Kind kind,
+                final Serializable parent,
+                final AjaxRequestTarget target) {
+            super(target);
+            this.key = key;
+            this.displayName = displayName;
+            this.kind = kind;
+            this.parent = parent;
+        }
+
+        public Serializable getKey() {
+            return key;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public TopologyNode.Kind getKind() {
+            return kind;
+        }
+
+        public Serializable getParent() {
+            return parent;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/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
index 80d05a1..837c89d 100644
--- 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
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.console.panels;
 
+import java.io.Serializable;
 import java.util.Collection;
 import java.util.List;
 import org.apache.syncope.client.console.commons.ActionTableCheckGroup;
@@ -37,7 +38,6 @@ 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> {
@@ -48,7 +48,7 @@ public class ActionDataTablePanel<T, S> extends DataTablePanel<T, S> {
 
     private final Form<T> bulkActionForm;
 
-    private final ActionLinksPanel actionPanel;
+    private final ActionLinksPanel<Serializable> actionPanel;
 
     private final PageReference pageRef;
 
@@ -93,7 +93,7 @@ public class ActionDataTablePanel<T, S> extends DataTablePanel<T, S> {
         final WebMarkupContainer actionPanelContainer = new WebMarkupContainer("actionPanelContainer");
         bulkActionForm.add(actionPanelContainer);
 
-        actionPanel = new ActionLinksPanel("actions", new Model(), pageRef);
+        actionPanel = ActionLinksPanel.builder(pageRef).build("actions");
         actionPanelContainer.add(actionPanel);
 
         if (dataTable.getRowCount() == 0) {
@@ -111,11 +111,13 @@ public class ActionDataTablePanel<T, S> extends DataTablePanel<T, S> {
         }.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<Serializable> action, final ActionType type, final String entitlements) {
+        actionPanel.add(action, type, entitlements, true);
     }
 
-    public void addAction(final ActionLink action, final ActionType type, final String pageId, final boolean enabled) {
+    public void addAction(
+            final ActionLink<Serializable> action, final ActionType type, final String pageId, final boolean enabled) {
         actionPanel.add(action, type, pageId, enabled);
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
new file mode 100644
index 0000000..a6859e8
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionModal.java
@@ -0,0 +1,36 @@
+/*
+ * 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.io.Serializable;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+
+/**
+ * Modal window with Resource form.
+ */
+public abstract class BeanReflectionModal extends ModalContent {
+
+    private static final long serialVersionUID = 1734415311027284222L;
+
+    public BeanReflectionModal(final Serializable bean, final ModalWindow window, final PageReference pageRef) {
+        super(window, pageRef);
+        add(new BeanReflectionPanel("bean", bean));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
new file mode 100644
index 0000000..4f3f1d5
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/BeanReflectionPanel.java
@@ -0,0 +1,339 @@
+/*
+ * 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.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.rest.PolicyRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AbstractFieldPanel;
+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.AjaxPalettePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.list.AltListView;
+import org.apache.syncope.common.lib.annotation.ClassList;
+import org.apache.syncope.common.lib.annotation.SchemaList;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.basic.Label;
+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.model.util.ListModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.ReflectionUtils.FieldCallback;
+import org.springframework.util.ReflectionUtils.FieldFilter;
+
+public class BeanReflectionPanel extends Panel {
+
+    private static final long serialVersionUID = -3035998190456928143L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(BeanReflectionPanel.class);
+
+    @SpringBean
+    private SchemaRestClient schemaRestClient;
+
+    @SpringBean
+    private PolicyRestClient policyRestClient;
+
+    private final IModel<List<String>> userSchemas = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = -2012833443695917883L;
+
+        @Override
+        protected List<String> load() {
+            return schemaRestClient.getPlainSchemaNames();
+        }
+    };
+
+    private final IModel<List<String>> groupSchemas = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return schemaRestClient.getPlainSchemaNames();
+        }
+    };
+
+    private final IModel<List<String>> correlationRules = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 5275935387613157437L;
+
+        @Override
+        protected List<String> load() {
+            return policyRestClient.getCorrelationRuleClasses();
+        }
+    };
+
+    public BeanReflectionPanel(final String id, final Serializable bean) {
+        super(id);
+
+        final List<FieldWrapper> items = new ArrayList<>();
+        ReflectionUtils.doWithFields(bean.getClass(),
+                new FieldCallback() {
+
+                    @Override
+                    public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
+                        FieldWrapper fieldWrapper = new FieldWrapper();
+                        fieldWrapper.setName(field.getName());
+                        fieldWrapper.setType(field.getType());
+
+                        final SchemaList schemaList = field.getAnnotation(SchemaList.class);
+                        fieldWrapper.setSchemaList(schemaList);
+
+                        final ClassList classList = field.getAnnotation(ClassList.class);
+                        fieldWrapper.setClassList(classList);
+
+                        items.add(fieldWrapper);
+                    }
+                },
+                new FieldFilter() {
+
+                    @Override
+                    public boolean matches(final Field field) {
+                        return !Modifier.isStatic(field.getModifiers()) && !"serialVersionUID".equals(field.getName());
+                    }
+                });
+
+        final ListView<FieldWrapper> policies = new AltListView<FieldWrapper>("fields", items) {
+
+            private static final long serialVersionUID = 9101744072914090143L;
+
+            @Override
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            protected void populateItem(final ListItem<FieldWrapper> item) {
+                final FieldWrapper field = item.getModelObject();
+
+                final PropertyDescriptor propDesc = BeanUtils.getPropertyDescriptor(bean.getClass(), field.getName());
+
+                item.add(new Label("label", new ResourceModel(field.getName())));
+
+                AbstractFieldPanel component;
+                try {
+                    if (field.getClassList() != null) {
+                        component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(bean,
+                                field.getName()));
+
+                        final List<String> rules = correlationRules.getObject();
+
+                        if (rules != null && !rules.isEmpty()) {
+                            ((AjaxDropDownChoicePanel) component).setChoices(correlationRules.getObject());
+                        }
+
+                        item.add(component);
+
+                        item.add(getActivationControl(
+                                component,
+                                propDesc.getReadMethod().invoke(bean, new Object[] {}) != null,
+                                null,
+                                null));
+
+                    } else if (field.getType().isEnum()) {
+                        component = new AjaxDropDownChoicePanel("field", field.getName(), new PropertyModel(bean,
+                                field.getName()));
+
+                        final Serializable[] values = (Serializable[]) field.getType().getEnumConstants();
+
+                        if (values != null && values.length > 0) {
+                            ((AjaxDropDownChoicePanel) component).setChoices(Arrays.asList(values));
+                        }
+
+                        item.add(component);
+
+                        item.add(getActivationControl(
+                                component,
+                                (Enum<?>) propDesc.getReadMethod().invoke(bean, new Object[] {}) != null,
+                                values[0],
+                                values[0]));
+
+                    } else if (ClassUtils.isAssignable(Boolean.class, field.getType())) {
+                        item.add(new AjaxCheckBoxPanel("check", field.getName(),
+                                new PropertyModel<Boolean>(bean, field.getName())));
+
+                        item.add(new Label("field", new Model(null)));
+                    } else if (Collection.class.isAssignableFrom(field.getType())) {
+                        if (field.getSchemaList() != null) {
+                            final List<String> values = new ArrayList<>();
+                            if (field.getName().charAt(0) == 'r') {
+                                values.addAll(groupSchemas.getObject());
+
+                                if (field.getSchemaList().extended()) {
+                                    values.add("name");
+                                }
+                            } else {
+                                values.addAll(userSchemas.getObject());
+
+                                if (field.getSchemaList().extended()) {
+                                    values.add("key");
+                                    values.add("username");
+                                }
+                            }
+
+                            component = new AjaxPalettePanel("field", new PropertyModel(bean, field.getName()),
+                                    new ListModel<>(values));
+                            item.add(component);
+
+                            Collection<?> collection = (Collection) propDesc.getReadMethod().invoke(bean);
+                            item.add(getActivationControl(component,
+                                    !collection.isEmpty(), new ArrayList<String>(), new ArrayList<String>()));
+                        } else {
+                            final FieldPanel panel = new AjaxTextFieldPanel("panel", field.getName(),
+                                    new Model<String>(null));
+                            panel.setRequired(true);
+
+                            component = new MultiFieldPanel<String>("field",
+                                    new PropertyModel(bean, field.getName()), panel);
+
+                            item.add(component);
+
+                            final List<String> reinitializedValue = new ArrayList<String>();
+
+                            reinitializedValue.add("");
+
+                            item.add(getActivationControl(component,
+                                    !((Collection) propDesc.getReadMethod().invoke(bean, new Object[] {})).isEmpty(),
+                                    new ArrayList<String>(), (Serializable) reinitializedValue));
+                        }
+                    } else if (ClassUtils.isAssignable(Number.class, field.getType())) {
+                        component = new SpinnerFieldPanel<Number>("field", field.getName(),
+                                (Class<Number>) field.getType(), new PropertyModel<Number>(bean, field.getName()),
+                                null, null);
+                        item.add(component);
+
+                        item.add(getActivationControl(component,
+                                (Integer) propDesc.getReadMethod().invoke(bean, new Object[] {}) > 0, 0, 0));
+                    } else if (field.getType().equals(String.class)) {
+                        component = new AjaxTextFieldPanel("field", field.getName(),
+                                new PropertyModel(bean, field.getName()));
+
+                        item.add(component);
+
+                        item.add(getActivationControl(component,
+                                propDesc.getReadMethod().invoke(bean, new Object[] {}) != null, null, null));
+                    } else {
+                        item.add(new AjaxCheckBoxPanel("check", field.getName(), new Model()));
+                        item.add(new Label("field", new Model(null)));
+                    }
+                } catch (Exception e) {
+                    LOG.error("Error retrieving bean fields", e);
+                }
+            }
+        };
+
+        add(policies);
+    }
+
+    private <T extends Serializable> AjaxCheckBoxPanel getActivationControl(final AbstractFieldPanel<T> panel,
+            final Boolean checked, final T defaultModelObject, final T reinitializedValue) {
+
+        final AjaxCheckBoxPanel check = new AjaxCheckBoxPanel("check", "check", new Model<Boolean>(checked));
+
+        panel.setEnabled(checked);
+
+        check.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                if (check.getModelObject()) {
+                    panel.setEnabled(true);
+                    panel.setModelObject(reinitializedValue);
+                } else {
+                    panel.setModelObject(defaultModelObject);
+                    panel.setEnabled(false);
+                }
+
+                target.add(panel);
+            }
+        });
+
+        return check;
+    }
+
+    private static class FieldWrapper implements Serializable {
+
+        private static final long serialVersionUID = -6770429509752964215L;
+
+        private Class<?> type;
+
+        private String name;
+
+        private transient SchemaList schemaList;
+
+        private transient ClassList classList;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(final String name) {
+            this.name = name;
+        }
+
+        public Class<?> getType() {
+            return type;
+        }
+
+        public void setType(final Class<?> type) {
+            this.type = type;
+        }
+
+        public SchemaList getSchemaList() {
+            return schemaList;
+        }
+
+        public void setSchemaList(final SchemaList schemaList) {
+            this.schemaList = schemaList;
+        }
+
+        public ClassList getClassList() {
+            return classList;
+        }
+
+        public void setClassList(final ClassList classList) {
+            this.classList = classList;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
index 40aefce..9db9bd2 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
@@ -30,8 +30,11 @@ import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.topology.Topology;
+import org.apache.syncope.client.console.topology.TopologyNode;
 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.CheckBoxMultipleChoiceFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.list.ConnConfPropertyListView;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -48,11 +51,11 @@ import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.Broadcast;
 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.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
 import org.apache.wicket.markup.html.form.DropDownChoice;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.list.ListView;
@@ -67,7 +70,7 @@ import org.apache.wicket.validation.validator.RangeValidator;
 /**
  * Modal window with Connector form.
  */
-public class ConnectorModal extends ModalContent {
+public class ConnectorModal extends AbstractResourceModal {
 
     private static final long serialVersionUID = -2025535531121434050L;
 
@@ -143,14 +146,14 @@ public class ConnectorModal extends ModalContent {
         connectorForm.add(displayName);
 
         final AjaxDropDownChoicePanel<String> location = new AjaxDropDownChoicePanel<>("location", "location",
-                new Model<>(bundleTO == null ? null : bundleTO.getLocation()));
+                new Model<>(bundleTO == null ? connInstanceTO.getLocation() : bundleTO.getLocation()));
         ((DropDownChoice<String>) location.getField()).setNullValid(true);
         location.setStyleSheet("long_dynamicsize");
         location.setChoices(new ArrayList<>(mapConnBundleTOs.keySet()));
         location.setRequired(true);
         location.addRequiredLabel();
         location.setOutputMarkupId(true);
-        location.setEnabled(connInstanceTO.getKey() == 0);
+        location.setEnabled(connInstanceTO.getKey() == 0 && StringUtils.isBlank(connInstanceTO.getLocation()));
         location.getField().setOutputMarkupId(true);
         connectorForm.add(location);
 
@@ -160,11 +163,12 @@ public class ConnectorModal extends ModalContent {
         ((DropDownChoice<String>) connectorName.getField()).setNullValid(true);
         connectorName.setStyleSheet("long_dynamicsize");
         connectorName.setChoices(bundleTO == null
-                ? new ArrayList<String>()
-                : new ArrayList<>(mapConnBundleTOs.get(connInstanceTO.getLocation()).keySet()));
+                ? StringUtils.isBlank(connInstanceTO.getLocation())
+                        ? new ArrayList<String>()
+                        : new ArrayList<>(mapConnBundleTOs.get(connInstanceTO.getLocation()).keySet())
+                : new ArrayList<>(mapConnBundleTOs.get(bundleTO.getLocation()).keySet()));
         connectorName.setRequired(true);
         connectorName.addRequiredLabel();
-        connectorName.setEnabled(connInstanceTO.getLocation() != null);
         connectorName.setOutputMarkupId(true);
         connectorName.setEnabled(connInstanceTO.getKey() == 0);
         connectorName.getField().setOutputMarkupId(true);
@@ -176,7 +180,7 @@ public class ConnectorModal extends ModalContent {
         version.setChoices(bundleTO == null
                 ? new ArrayList<String>()
                 : new ArrayList<>(mapConnBundleTOs.get(connInstanceTO.getLocation()).
-                get(connInstanceTO.getBundleName()).keySet()));
+                        get(connInstanceTO.getBundleName()).keySet()));
         version.setRequired(true);
         version.addRequiredLabel();
         version.setEnabled(connInstanceTO.getBundleName() != null);
@@ -313,19 +317,20 @@ public class ConnectorModal extends ModalContent {
         connectorPropForm.add(check);
 
         // form - third tab (capabilities)
-        final IModel<List<ConnectorCapability>> capabilities =
-                new LoadableDetachableModel<List<ConnectorCapability>>() {
+        final IModel<List<ConnectorCapability>> capabilities
+                = new LoadableDetachableModel<List<ConnectorCapability>>() {
 
-            private static final long serialVersionUID = 5275935387613157437L;
+                    private static final long serialVersionUID = 5275935387613157437L;
 
-            @Override
-            protected List<ConnectorCapability> load() {
-                return Arrays.asList(ConnectorCapability.values());
-            }
-        };
-        CheckBoxMultipleChoice<ConnectorCapability> capabilitiesPalette = new CheckBoxMultipleChoice<>(
-                "capabilitiesPalette",
-                new PropertyModel<List<ConnectorCapability>>(this, "selectedCapabilities"), capabilities);
+                    @Override
+                    protected List<ConnectorCapability> load() {
+                        return Arrays.asList(ConnectorCapability.values());
+                    }
+                };
+        CheckBoxMultipleChoiceFieldPanel<ConnectorCapability> capabilitiesPalette
+                = new CheckBoxMultipleChoiceFieldPanel<>(
+                        "capabilitiesPalette",
+                        new PropertyModel<List<ConnectorCapability>>(this, "selectedCapabilities"), capabilities);
 
         capabilitiesPalette.add(new AjaxFormChoiceComponentUpdatingBehavior() {
 
@@ -373,6 +378,13 @@ public class ConnectorModal extends ModalContent {
                 try {
                     if (connInstanceTO.getKey() == 0) {
                         connectorRestClient.create(conn);
+                        send(pageRef.getPage(), Broadcast.BREADTH, new CreateEvent(
+                                conn.getKey(),
+                                conn.getDisplayName(),
+                                TopologyNode.Kind.CONNECTOR,
+                                conn.getLocation().startsWith(Topology.CONNECTOR_SERVER_LOCATION_PREFIX)
+                                        ? conn.getLocation() : Topology.ROOT_NAME,
+                                target));
                     } else {
                         connectorRestClient.update(conn);
                     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/ListViewPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ListViewPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ListViewPanel.java
new file mode 100644
index 0000000..f89aa9b
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ListViewPanel.java
@@ -0,0 +1,322 @@
+/*
+ * 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.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+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.ActionLinksPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+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.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.ResourceModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ListViewPanel<T extends Serializable> extends Panel {
+
+    private static final long serialVersionUID = -7982691107029848579L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(ListViewPanel.class);
+
+    private final ClearIndicatingAjaxButton addButton;
+
+    private AjaxWizard<T> newItemPanel;
+
+    private final WebMarkupContainer container;
+
+    private final Fragment initialFragment;
+
+    private final List<T> listOfItems;
+
+    /**
+     * Table view of a list of beans.
+     *
+     * @param id id.
+     * @param list list of item.
+     * @param reference list item reference class.
+     * @param includes Used to sort and restrict the set of bean's fields to be shown.
+     * @param actions item actions.
+     */
+    private ListViewPanel(
+            final String id,
+            final List<T> list,
+            final Class<T> reference,
+            final List<String> includes,
+            final ActionLinksPanel.Builder<T> actions,
+            final PageReference pageRef) {
+        super(id);
+        setOutputMarkupId(true);
+
+        container = new WebMarkupContainer("container");
+        add(container.setOutputMarkupId(true));
+
+        initialFragment = new Fragment("content", "table", this);
+        container.addOrReplace(initialFragment);
+
+        initialFragment.add(new Label("caption", new ResourceModel("listview.caption", StringUtils.EMPTY)));
+
+        final List<String> toBeIncluded;
+        if (includes == null || includes.isEmpty()) {
+            toBeIncluded = new ArrayList<String>();
+            for (Field field : Arrays.asList(reference.getDeclaredFields())) {
+                toBeIncluded.add(field.getName());
+            }
+        } else {
+            toBeIncluded = includes;
+        }
+
+        if (toBeIncluded.isEmpty()) {
+            LOG.warn("No field has been retrieved from {}", reference.getName());
+            listOfItems = Collections.<T>emptyList();
+        } else if (list == null || list.isEmpty()) {
+            LOG.info("No item to be shown");
+            listOfItems = Collections.<T>emptyList();
+        } else {
+            listOfItems = list;
+            if (LOG.isDebugEnabled()) {
+                for (String field : toBeIncluded) {
+                    LOG.debug("Show field {}", field);
+                }
+            }
+        }
+
+        final ListView<String> names = new ListView<String>("names", toBeIncluded) {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void populateItem(final ListItem<String> item) {
+                item.add(new Label("name", new ResourceModel(item.getModelObject(), item.getModelObject())));
+            }
+        };
+        initialFragment.add(names);
+
+        final ListView<T> beans = new ListView<T>("beans", listOfItems) {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void populateItem(final ListItem<T> beanItem) {
+                final T bean = beanItem.getModelObject();
+
+                final ListView<String> fields = new ListView<String>("fields", toBeIncluded) {
+
+                    private static final long serialVersionUID = 1L;
+
+                    @Override
+                    protected void populateItem(final ListItem<String> fieldItem) {
+                        try {
+                            LOG.error("Processing field {}", fieldItem.getModelObject());
+
+                            final Object value = new PropertyDescriptor(fieldItem.getModelObject(), bean.getClass()).
+                                    getReadMethod().invoke(bean);
+
+                            LOG.error("Field value {}", value);
+
+                            fieldItem.add(value == null
+                                    ? new Label("field", StringUtils.EMPTY)
+                                    : new Label("field", new ResourceModel(value.toString(), value.toString())));
+
+                        } catch (Exception e) {
+                            LOG.error("Error retrieving value for field {}", fieldItem.getModelObject(), e);
+                            fieldItem.add(new Label("field", StringUtils.EMPTY));
+                        }
+                    }
+                };
+                beanItem.add(fields);
+                beanItem.add(actions.build("actions", bean));
+            }
+        };
+        beans.setReuseItems(true);
+        initialFragment.add(beans);
+
+        addButton = new ClearIndicatingAjaxButton("add", pageRef) {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                final Fragment fragment = new Fragment("content", "wizard", ListViewPanel.this);
+                fragment.add(newItemPanel.clone());
+                container.addOrReplace(fragment);
+                target.add(container);
+            }
+        };
+
+        addButton.setEnabled(false);
+        addButton.setVisible(false);
+
+        initialFragment.add(addButton);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
+            final AjaxRequestTarget target = AjaxWizard.NewItemEvent.class.cast(event.getPayload()).getTarget();
+
+            @SuppressWarnings("unchecked")
+            final T item = ((AjaxWizard.NewItemEvent<T>) event.getPayload()).getItem();
+
+            if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
+                this.listOfItems.add(item);
+            }
+
+            container.addOrReplace(initialFragment);
+            target.add(container);
+        }
+        super.onEvent(event);
+    }
+
+    private ListViewPanel<T> addNewItemPanel(final AjaxWizard<T> panel) {
+        this.newItemPanel = panel;
+
+        if (this.newItemPanel != null) {
+            addButton.setEnabled(true);
+            addButton.setVisible(true);
+        }
+
+        return this;
+    }
+
+    public static <T extends Serializable> Builder<T> builder(final Class<T> reference, final PageReference pageRef) {
+        return new Builder<T>(reference, pageRef);
+    }
+
+    /**
+     * ListViewPanel builder.
+     *
+     * @param <T> list item reference type.
+     */
+    public static final class Builder<T extends Serializable> implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        private final PageReference pageRef;
+
+        private final Class<T> reference;
+
+        private final List<String> includes = new ArrayList<>();
+
+        private final ActionLinksPanel.Builder<T> actions;
+
+        private List<T> items;
+
+        private AjaxWizard<T> newItemPanel;
+
+        private Builder(final Class<T> reference, final PageReference pageRef) {
+            this.pageRef = pageRef;
+            this.reference = reference;
+            this.items = null;
+            this.actions = ActionLinksPanel.<T>builder(pageRef);
+        }
+
+        /**
+         * Builds a list view.
+         *
+         * @param id component id.
+         * @return List view.
+         */
+        public ListViewPanel<T> build(final String id) {
+            return new ListViewPanel<T>(id, items, reference, includes, actions, pageRef).addNewItemPanel(newItemPanel);
+        }
+
+        /**
+         * Sets list of items.
+         *
+         * @param items list of items.
+         * @return current builder object.
+         */
+        public Builder<T> setItems(final List<T> items) {
+            this.items = items;
+            return this;
+        }
+
+        /**
+         * Adds item.
+         *
+         * @param item item.
+         * @return current builder object.
+         */
+        public Builder<T> addItem(final T item) {
+            if (item == null) {
+                return this;
+            }
+
+            if (this.items == null) {
+                this.items = new ArrayList<>();
+            }
+
+            this.items.add(item);
+            return this;
+        }
+
+        /**
+         * Gives fields to be shown. It could be used to give an order as well.
+         *
+         * @param includes field names to be shown.
+         * @return current builder object.
+         */
+        public Builder<T> includes(final String... includes) {
+            for (String include : includes) {
+                if (include != null && !this.includes.contains(include)) {
+                    this.includes.add(include);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Add item action (the given order is ignored.
+         *
+         * @param link action link.
+         * @param type action type.
+         * @param entitlements entitlements.
+         * @return current builder object.
+         */
+        public Builder<T> addAction(
+                final ActionLink<T> link, final ActionLink.ActionType type, final String entitlements) {
+            actions.add(link, type, entitlements);
+            return this;
+        }
+
+        public Builder<T> addNewItemPanel(final AjaxWizard<T> panel) {
+            this.newItemPanel = panel;
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/ModalContent.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ModalContent.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ModalContent.java
index fbf8802..6a123c0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ModalContent.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ModalContent.java
@@ -54,9 +54,9 @@ public class ModalContent extends Panel {
 
     protected NotificationPanel feedbackPanel;
 
-    private final PageReference pageRef;
+    protected final PageReference pageRef;
 
-    private final ModalWindow window;
+    protected final ModalWindow window;
 
     public ModalContent(final ModalWindow window, final PageReference pageRef) {
         super(window.getContentId());
@@ -68,6 +68,10 @@ public class ModalContent extends Panel {
         add(feedbackPanel);
     }
 
+    public NotificationPanel getFeedbackPanel() {
+        return feedbackPanel;
+    }
+
     /**
      * Generic modal event.
      */

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
index 17ae3bf..b210c86 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceMappingPanel.java
@@ -120,9 +120,9 @@ public class ResourceMappingPanel extends Panel {
     private final ResourceTO resourceTO;
 
     /**
-     * User / group.
+     * External resource provisioning configuration instance to be updated.
      */
-    private final AnyTypeKind anyTypeKind;
+    private final ProvisionTO provisionTO;
 
     /**
      * Mapping container.
@@ -134,31 +134,26 @@ public class ResourceMappingPanel extends Panel {
     private final AjaxCheckBoxPanel connObjectLinkCheckbox;
 
     private MappingTO getMapping() {
-        ProvisionTO provision = resourceTO.getProvision(this.anyTypeKind.name());
-        if (provision == null) {
-            provision = new ProvisionTO();
-            resourceTO.getProvisions().add(provision);
-        }
-        if (provision.getMapping() == null) {
-            provision.setMapping(new MappingTO());
+        if (provisionTO.getMapping() == null) {
+            provisionTO.setMapping(new MappingTO());
         }
 
-        return provision.getMapping();
+        return provisionTO.getMapping();
     }
 
     /**
      * Attribute Mapping Panel.
      *
      * @param id panel id
-     * @param resourceTO external resource
-     * @param anyTypeKind USER / GROUP
+     * @param resourceTO external resource to be updated.
+     * @param provisionTO external resource provisioning configuration instance.
      */
-    public ResourceMappingPanel(final String id, final ResourceTO resourceTO, final AnyTypeKind anyTypeKind) {
+    public ResourceMappingPanel(final String id, final ResourceTO resourceTO, final ProvisionTO provisionTO) {
         super(id);
         setOutputMarkupId(true);
 
         this.resourceTO = resourceTO;
-        this.anyTypeKind = anyTypeKind;
+        this.provisionTO = provisionTO == null ? new ProvisionTO() : provisionTO;
 
         this.mappingContainer = new WebMarkupContainer("mappingContainer");
         this.mappingContainer.setOutputMarkupId(true);
@@ -168,9 +163,8 @@ public class ResourceMappingPanel extends Panel {
         this.connObjectLinkContainer.setOutputMarkupId(true);
         add(this.connObjectLinkContainer);
 
-        if (this.resourceTO.getConnector() != null && this.resourceTO.getConnector() > 0) {
-            schemaNames = getSchemaNames(this.resourceTO.getConnector(), this.resourceTO.getConnConfProperties());
-
+        if (resourceTO.getConnector() != null && resourceTO.getConnector() > 0) {
+            schemaNames = getSchemaNames(resourceTO.getConnector(), resourceTO.getConnConfProperties());
             setEnabled();
         } else {
             schemaNames = Collections.<String>emptyList();
@@ -184,7 +178,8 @@ public class ResourceMappingPanel extends Panel {
 
         final Label passwordLabel = new Label("passwordLabel", new ResourceModel("password"));
         mappingContainer.add(passwordLabel);
-        if (AnyTypeKind.USER != ResourceMappingPanel.this.anyTypeKind) {
+
+        if (!AnyTypeKind.USER.name().equals(this.provisionTO.getAnyType())) {
             passwordLabel.setVisible(false);
         }
 
@@ -284,9 +279,9 @@ public class ResourceMappingPanel extends Panel {
                     }
                 });
 
-                final AjaxDropDownChoicePanel<String> intAttrNames =
-                        new AjaxDropDownChoicePanel<>("intAttrNames", getString("intAttrNames"),
-                                new PropertyModel<String>(mapItem, "intAttrName"), false);
+                final AjaxDropDownChoicePanel<String> intAttrNames = new AjaxDropDownChoicePanel<>("intAttrNames",
+                        getString("intAttrNames"),
+                        new PropertyModel<String>(mapItem, "intAttrName"), false);
                 intAttrNames.setChoices(schemaNames);
                 intAttrNames.setRequired(true);
                 intAttrNames.setStyleSheet(FIELD_STYLE);
@@ -301,22 +296,23 @@ public class ResourceMappingPanel extends Panel {
                 item.add(intAttrNames);
 
                 final List<IntMappingType> attrTypes = new ArrayList<>(getAttributeTypes(entity));
-                final AjaxDropDownChoicePanel<IntMappingType> intMappingTypes =
-                        new AjaxDropDownChoicePanel<>("intMappingTypes",
-                                new ResourceModel("intMappingTypes", "intMappingTypes").getObject(),
-                                new PropertyModel<IntMappingType>(mapItem, "intMappingType"));
+                final AjaxDropDownChoicePanel<IntMappingType> intMappingTypes = new AjaxDropDownChoicePanel<>(
+                        "intMappingTypes",
+                        new ResourceModel("intMappingTypes", "intMappingTypes").getObject(),
+                        new PropertyModel<IntMappingType>(mapItem, "intMappingType"));
                 intMappingTypes.setRequired(true);
                 intMappingTypes.setChoices(attrTypes);
                 intMappingTypes.setStyleSheet(FIELD_STYLE);
                 item.add(intMappingTypes);
 
-                final AjaxDropDownChoicePanel<AnyTypeKind> entitiesPanel =
-                        new AjaxDropDownChoicePanel<>("entities",
-                                new ResourceModel("entities", "entities").getObject(),
-                                new Model<>(entity));
-                entitiesPanel.setChoices(anyTypeKind == AnyTypeKind.GROUP
+                final AjaxDropDownChoicePanel<AnyTypeKind> entitiesPanel = new AjaxDropDownChoicePanel<>("entities",
+                        new ResourceModel("entities", "entities").getObject(),
+                        new Model<>(entity));
+
+                entitiesPanel.setChoices(provisionTO.getAnyType().equals(AnyTypeKind.GROUP.name())
                         ? Collections.<AnyTypeKind>singletonList(AnyTypeKind.GROUP)
                         : Arrays.asList(AnyTypeKind.values()));
+
                 entitiesPanel.setStyleSheet(DEF_FIELD_STYLE);
                 entitiesPanel.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 
@@ -399,7 +395,7 @@ public class ResourceMappingPanel extends Panel {
                     }
                 });
                 item.add(password);
-                if (AnyTypeKind.USER != ResourceMappingPanel.this.anyTypeKind) {
+                if (!AnyTypeKind.USER.name().equals(provisionTO.getAnyType())) {
                     password.setVisible(false);
                 }
 
@@ -446,7 +442,7 @@ public class ResourceMappingPanel extends Panel {
             }
         };
         addMappingBtn.setDefaultFormProcessing(false);
-        addMappingBtn.setEnabled(this.resourceTO.getConnector() != null && this.resourceTO.getConnector() > 0);
+        addMappingBtn.setEnabled(resourceTO.getConnector() != null && resourceTO.getConnector() > 0);
         mappingContainer.add(addMappingBtn);
 
         boolean connObjectLinkEnabled = false;
@@ -495,10 +491,10 @@ public class ResourceMappingPanel extends Panel {
 
     private void setEnabled() {
         ConnInstanceTO connInstanceTO = new ConnInstanceTO();
-        connInstanceTO.setKey(this.resourceTO.getConnector());
-        connInstanceTO.getConfiguration().addAll(this.resourceTO.getConnConfProperties());
+        connInstanceTO.setKey(resourceTO.getConnector());
+        connInstanceTO.getConfiguration().addAll(resourceTO.getConnConfProperties());
 
-        boolean enabled = resourceTO.getProvision(anyTypeKind.name()) != null;
+        boolean enabled = provisionTO != null;
 
         this.mappingContainer.setEnabled(enabled);
         this.mappingContainer.setVisible(enabled);

http://git-wip-us.apache.org/repos/asf/syncope/blob/ba7f1a5c/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
index 8627998..6390f01 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
@@ -26,10 +26,12 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.pages.AbstractBasePage;
+import org.apache.syncope.client.console.topology.TopologyNode;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wizards.ProvisionWizard;
 import org.apache.syncope.common.lib.to.MappingItemTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
@@ -38,6 +40,7 @@ import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDa
 import org.apache.wicket.event.Broadcast;
 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.WebMarkupContainer;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.ResourceModel;
@@ -45,7 +48,7 @@ import org.apache.wicket.model.ResourceModel;
 /**
  * Modal window with Resource form.
  */
-public class ResourceModal extends ModalContent {
+public class ResourceModal extends AbstractResourceModal {
 
     private static final long serialVersionUID = 1734415311027284221L;
 
@@ -71,10 +74,61 @@ public class ResourceModal extends ModalContent {
         //--------------------------------
 
         //--------------------------------
-        // Resource mapping panels
+        // Resource provision panels
         //--------------------------------
-        form.add(new ResourceMappingPanel("umapping", resourceTO, AnyTypeKind.USER));
-        form.add(new ResourceMappingPanel("gmapping", resourceTO, AnyTypeKind.GROUP));
+        final WebMarkupContainer provisions = new WebMarkupContainer("pcontainer");
+        form.add(provisions.setOutputMarkupId(true));
+
+        final ListViewPanel.Builder<ProvisionTO> builder = ListViewPanel.builder(ProvisionTO.class, pageRef);
+        builder.setItems(resourceTO.getProvisions());
+        builder.includes("anyType", "objectClass");
+
+        builder.addAction(new ActionLink<ProvisionTO>() {
+
+            private static final long serialVersionUID = -3722207913631435504L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ProvisionTO provisionTO) {
+
+            }
+        }, ActionLink.ActionType.MAPPING, Entitlement.RESOURCE_UPDATE).addAction(new ActionLink<ProvisionTO>() {
+
+            private static final long serialVersionUID = -3722207913631435514L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ProvisionTO provisionTO) {
+
+            }
+        }, ActionLink.ActionType.ACCOUNT_LINK, Entitlement.RESOURCE_UPDATE).addAction(new ActionLink<ProvisionTO>() {
+
+            private static final long serialVersionUID = -3722207913631435524L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ProvisionTO provisionTO) {
+
+            }
+        }, ActionLink.ActionType.RESET_TIME, Entitlement.RESOURCE_UPDATE).addAction(new ActionLink<ProvisionTO>() {
+
+            private static final long serialVersionUID = -3722207913631435534L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ProvisionTO provisionTO) {
+
+            }
+        }, ActionLink.ActionType.CLONE, Entitlement.RESOURCE_CREATE).addAction(new ActionLink<ProvisionTO>() {
+
+            private static final long serialVersionUID = -3722207913631435544L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final ProvisionTO provisionTO) {
+                resourceTO.getProvisions().remove(provisionTO);
+                target.add(provisions);
+
+            }
+        }, ActionLink.ActionType.DELETE, Entitlement.RESOURCE_DELETE);
+
+        builder.addNewItemPanel(new ProvisionWizard("wizard", resourceTO, pageRef));
+        provisions.add(builder.build("provisions"));
         //--------------------------------
 
         //--------------------------------
@@ -129,7 +183,12 @@ public class ResourceModal extends ModalContent {
                     try {
                         if (createFlag) {
                             resourceRestClient.create(resourceTO);
-                            send(pageRef.getPage(), Broadcast.BREADTH, new ResourceCreateEvent(target, resourceTO));
+                            send(pageRef.getPage(), Broadcast.BREADTH, new CreateEvent(
+                                    resourceTO.getKey(),
+                                    resourceTO.getKey(),
+                                    TopologyNode.Kind.RESOURCE,
+                                    resourceTO.getConnector(),
+                                    target));
                         } else {
                             resourceRestClient.update(resourceTO);
                         }
@@ -178,27 +237,4 @@ public class ResourceModal extends ModalContent {
         MetaDataRoleAuthorizationStrategy.authorize(
                 submit, ENABLE, createFlag ? Entitlement.RESOURCE_CREATE : Entitlement.RESOURCE_UPDATE);
     }
-
-    public NotificationPanel getFeedbackPanel() {
-        return feedbackPanel;
-    }
-
-    public static class ResourceCreateEvent extends ModalEvent {
-
-        private final ResourceTO resourceTO;
-
-        public ResourceCreateEvent(final AjaxRequestTarget target, final ResourceTO resourceTO) {
-            super(target);
-            this.resourceTO = resourceTO;
-        }
-
-        /**
-         * Create resource getter.
-         *
-         * @return created resource.
-         */
-        public ResourceTO getResourceTO() {
-            return resourceTO;
-        }
-    }
 }