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 2022/02/15 15:34:17 UTC

[syncope] branch 2_1_X updated: [SYNCOPE-1658] Tabular Topology (#312)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/2_1_X by this push:
     new 0107a1c  [SYNCOPE-1658] Tabular Topology (#312)
0107a1c is described below

commit 0107a1c304ca9a5e85c64acf57290918facb303c
Author: SamuelGaro <72...@users.noreply.github.com>
AuthorDate: Tue Feb 15 16:34:08 2022 +0100

    [SYNCOPE-1658] Tabular Topology (#312)
---
 .../client/console/SyncopeConsoleApplication.java  |  10 +
 .../console/commons/ConnectorDataProvider.java     | 152 ++++++++
 .../console/commons/ResourceDataProvider.java      | 152 ++++++++
 .../syncope/client/console/pages/BasePage.java     |   7 +-
 .../syncope/client/console/pages/Connectors.java   |  84 ++++
 .../syncope/client/console/pages/Resources.java    |  84 ++++
 .../console/panels/ConnectorDirectoryPanel.java    | 283 ++++++++++++++
 .../client/console/panels/ConnidLocations.java     | 162 ++++++++
 .../console/panels/ResourceDirectoryPanel.java     | 432 +++++++++++++++++++++
 .../console/tasks/SchedTaskDirectoryPanel.java     | 113 ++++++
 .../syncope/client/console/tasks/SchedTasks.java   |  21 +
 .../client/console/tasks/TaskDirectoryPanel.java   |  20 +
 .../client/console/topology/TabularTopology.java   | 113 ++++++
 .../wicket/markup/html/form/ActionLink.java        |   8 +-
 .../client/console/widgets/CompletenessWidget.java |  11 +-
 .../client/console/widgets/NumberWidget.java       |   8 +-
 .../client/console/wizards/WizardMgtPanel.java     |   4 +-
 .../wizards/resources/ConnectorDetailsPanel.java   |  68 +++-
 .../wizards/resources/ConnectorWizardBuilder.java  |  13 +-
 .../wizards/resources/ResourceConnConfPanel.java   |   2 +-
 .../wizards/resources/ResourceDetailsPanel.java    |  52 ++-
 .../wizards/resources/ResourceWizardBuilder.java   |  11 +-
 .../syncope/client/console/pages/Connectors.html   |  42 ++
 .../syncope/client/console/pages/Resources.html    |  42 ++
 .../panels/ConnectorDirectoryPanel.properties      |  27 ++
 .../ConnectorDirectoryPanel_fr_CA.properties       |  27 ++
 .../panels/ConnectorDirectoryPanel_it.properties   |  27 ++
 .../panels/ConnectorDirectoryPanel_ja.properties   |  27 ++
 .../ConnectorDirectoryPanel_pt_BR.properties       |  27 ++
 .../panels/ConnectorDirectoryPanel_ru.properties   |  27 ++
 .../console/panels/ConnidLocations.properties      |  18 +
 .../panels/ConnidLocations_fr_CA.properties        |  18 +
 .../console/panels/ConnidLocations_it.properties   |  18 +
 .../console/panels/ConnidLocations_ja.properties   |  18 +
 .../panels/ConnidLocations_pt_BR.properties        |  18 +
 .../console/panels/ConnidLocations_ru.properties   |  18 +
 .../panels/ResourceDirectoryPanel.properties       |  38 ++
 .../panels/ResourceDirectoryPanel_fr_CA.properties |  38 ++
 .../panels/ResourceDirectoryPanel_it.properties    |  38 ++
 .../panels/ResourceDirectoryPanel_ja.properties    |  38 ++
 .../panels/ResourceDirectoryPanel_pt_BR.properties |  38 ++
 .../panels/ResourceDirectoryPanel_ru.properties    |  38 ++
 .../client/console/topology/TabularTopology.html   |  27 ++
 .../markup/html/form/ActionsPanel.properties       |  24 ++
 .../markup/html/form/ActionsPanel_fr_CA.properties |  27 ++
 .../markup/html/form/ActionsPanel_it.properties    |  24 ++
 .../markup/html/form/ActionsPanel_ja.properties    |  24 ++
 .../markup/html/form/ActionsPanel_pt_BR.properties |  24 ++
 .../markup/html/form/ActionsPanel_ru.properties    |  24 ++
 49 files changed, 2540 insertions(+), 26 deletions(-)

diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
index f3cd130..15e96d5 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java
@@ -46,6 +46,7 @@ import org.apache.syncope.client.console.pages.MustChangePassword;
 import org.apache.syncope.client.console.pages.Login;
 import org.apache.syncope.client.console.panels.AnyPanel;
 import org.apache.syncope.client.console.themes.AdminLTE;
+import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.lib.PropertyUtils;
@@ -120,6 +121,8 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication {
 
     private String defaultAnyLayoutClass;
 
+    private String defaultTopologyClass;
+
     @SuppressWarnings("unchecked")
     protected void populatePageClasses(final Properties props) {
         Enumeration<String> propNames = (Enumeration<String>) props.propertyNames();
@@ -185,6 +188,9 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication {
         maxPoolSize = Integer.valueOf(props.getProperty("topology.maxPoolSize", "10"));
         queueCapacity = Integer.valueOf(props.getProperty("topology.queueCapacity", "50"));
 
+        // topology page
+        defaultTopologyClass = props.getProperty("page.topology", Topology.class.getName());
+        
         // process page properties
         pageClasses = new HashMap<>();
         populatePageClasses(props);
@@ -334,6 +340,10 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication {
         return defaultAnyLayoutClass;
     }
 
+    public String getDefaultTopologyClass() {
+        return defaultTopologyClass;
+    }
+
     public SyncopeClientFactoryBean newClientFactory() {
         return new SyncopeClientFactoryBean().
                 setAddress(scheme + "://" + host + ":" + port + StringUtils.prependIfMissing(rootPath, "/")).
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java
new file mode 100644
index 0000000..f57650e
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/ConnectorDataProvider.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.commons;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class ConnectorDataProvider extends DirectoryDataProvider<Serializable> {
+
+    private static final long serialVersionUID = 3122389673525690470L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(ConnectorDataProvider.class);
+
+    private final PageReference pageRef;
+
+    protected int currentPage;
+
+    private String keyword;
+
+    private final ConnectorRestClient restClient = new ConnectorRestClient();
+
+    public ConnectorDataProvider(
+            final int paginatorRows,
+            final PageReference pageRef,
+            final String keyword) {
+        super(paginatorRows);
+        setSort("displayNameSortParam", SortOrder.ASCENDING);
+        this.pageRef = pageRef;
+        this.keyword = keyword;
+    }
+
+    @Override
+    public Iterator<ConnInstanceTO> iterator(final long first, final long count) {
+        List<ConnInstanceTO> result = Collections.emptyList();
+
+        try {
+            currentPage = ((int) first / paginatorRows);
+            if (currentPage < 0) {
+                currentPage = 0;
+            }
+            if (StringUtils.isBlank(keyword)) {
+                result = restClient.getAllConnectors();
+            } else {
+                result = restClient.getAllConnectors().stream().filter(conn ->
+                        conn.getDisplayName().toLowerCase().contains(keyword)).collect(Collectors.toList());
+            }
+        } catch (Exception e) {
+            LOG.error("While searching", e);
+            SyncopeConsoleSession.get().onException(e);
+
+            Optional<AjaxRequestTarget> target = RequestCycle.get().find(AjaxRequestTarget.class);
+            target.ifPresent(ajaxRequestTarget ->
+                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(ajaxRequestTarget));
+        }
+
+        SortParam<String> sortParam = getSort();
+        if (sortParam != null) {
+            result.sort(getComparator(sortParam));
+        }
+
+        return result.subList((int) first, (int) first + (int) count).iterator();
+    }
+
+    private Comparator<ConnInstanceTO> getComparator(final SortParam<String> sortParam) {
+        Comparator<ConnInstanceTO> comparator;
+
+        switch (sortParam.getProperty()) {
+            case "displayNameSortParam":
+                comparator = Comparator.nullsFirst(Comparator.comparing(
+                        item -> item.getDisplayName().toLowerCase()));
+                break;
+            case "connectorNameSortParam":
+                comparator = Comparator.nullsFirst(Comparator.comparing(
+                        item -> item.getConnectorName().toLowerCase()));
+                break;
+            default:
+                throw new IllegalStateException("The sort param " + sortParam.getProperty() + " is not correct");
+        }
+
+        if (!sortParam.isAscending()) {
+            comparator = comparator.reversed();
+        }
+
+        return comparator;
+    }
+
+    @Override
+    public long size() {
+        long result = 0;
+
+        try {
+            if (StringUtils.isBlank(keyword)) {
+                result = restClient.getAllConnectors().size();
+            } else {
+                result = restClient.getAllConnectors().stream().filter(conn ->
+                        conn.getDisplayName().toLowerCase().contains(keyword)).count();
+            }
+        } catch (Exception e) {
+            LOG.error("While requesting for size()", e);
+            SyncopeConsoleSession.get().onException(e);
+
+            RequestCycle.get().find(AjaxRequestTarget.class).
+                    ifPresent(target -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target));
+        }
+
+        return result;
+    }
+
+    @Override
+    public IModel<Serializable> model(final Serializable object) {
+        return new CompoundPropertyModel<>((ConnInstanceTO) object);
+    }
+
+    public int getCurrentPage() {
+        return currentPage;
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java
new file mode 100644
index 0000000..ba6990d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/ResourceDataProvider.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.commons;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class ResourceDataProvider extends DirectoryDataProvider<Serializable> {
+
+    private static final long serialVersionUID = 3189980210236051840L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(ResourceDataProvider.class);
+
+    private final PageReference pageRef;
+
+    protected int currentPage;
+
+    private String keyword;
+
+    private final ResourceRestClient restClient = new ResourceRestClient();
+
+    public ResourceDataProvider(
+            final int paginatorRows,
+            final PageReference pageRef,
+            final String keyword) {
+        super(paginatorRows);
+        setSort("keySortParam", SortOrder.ASCENDING);
+        this.pageRef = pageRef;
+        this.keyword = keyword;
+    }
+
+    @Override
+    public Iterator<ResourceTO> iterator(final long first, final long count) {
+        List<ResourceTO> result = Collections.emptyList();
+
+        try {
+            currentPage = ((int) first / paginatorRows);
+            if (currentPage < 0) {
+                currentPage = 0;
+            }
+            if (StringUtils.isBlank(keyword)) {
+                result = restClient.list();
+            } else {
+                result = restClient.list().stream().filter(resource ->
+                        resource.getKey().toLowerCase().contains(keyword)).collect(Collectors.toList());
+            }
+        } catch (Exception e) {
+            LOG.error("While searching", e);
+            SyncopeConsoleSession.get().onException(e);
+
+            Optional<AjaxRequestTarget> target = RequestCycle.get().find(AjaxRequestTarget.class);
+            target.ifPresent(ajaxRequestTarget ->
+                    ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(ajaxRequestTarget));
+        }
+
+        SortParam<String> sortParam = getSort();
+        if (sortParam != null) {
+            result.sort(getComparator(sortParam));
+        }
+
+        return result.subList((int) first, (int) first + (int) count).iterator();
+    }
+
+    private Comparator<ResourceTO> getComparator(final SortParam<String> sortParam) {
+        Comparator<ResourceTO> comparator;
+
+        switch (sortParam.getProperty()) {
+            case "keySortParam":
+                comparator = Comparator.nullsFirst(Comparator.comparing(
+                        item -> item.getKey().toLowerCase()));
+                break;
+            case "connectorDisplayNameSortParam":
+                comparator = Comparator.nullsFirst(Comparator.comparing(
+                        item -> item.getConnectorDisplayName().toLowerCase()));
+                break;
+            default:
+                throw new IllegalStateException("The sort param " + sortParam.getProperty() + " is not correct");
+        }
+
+        if (!sortParam.isAscending()) {
+            comparator = comparator.reversed();
+        }
+
+        return comparator;
+    }
+
+    @Override
+    public long size() {
+        long result = 0;
+
+        try {
+            if (StringUtils.isBlank(keyword)) {
+                result = restClient.list().size();
+            } else {
+                result = restClient.list().stream().filter(resource ->
+                        resource.getKey().toLowerCase().contains(keyword)).count();
+            }
+        } catch (Exception e) {
+            LOG.error("While requesting for size()", e);
+            SyncopeConsoleSession.get().onException(e);
+
+            RequestCycle.get().find(AjaxRequestTarget.class).
+                    ifPresent(target -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target));
+        }
+
+        return result;
+    }
+
+    @Override
+    public IModel<Serializable> model(final Serializable object) {
+        return new CompoundPropertyModel<>((ResourceTO) object);
+    }
+
+    public int getCurrentPage() {
+        return currentPage;
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
index a464a11..fa893b1 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/BasePage.java
@@ -34,6 +34,7 @@ import org.apache.syncope.client.console.panels.DelegationSelectionPanel;
 import org.apache.syncope.client.console.panels.NotificationPanel;
 import org.apache.syncope.client.console.rest.ConfRestClient;
 import org.apache.syncope.client.console.rest.ResponseHolder;
+import org.apache.syncope.client.console.topology.TabularTopology;
 import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.client.console.wicket.markup.head.MetaHeaderItem;
 import org.apache.syncope.client.console.wicket.markup.html.form.IndicatingOnConfirmAjaxLink;
@@ -173,7 +174,11 @@ public class BasePage extends WebPage implements IAjaxIndicatorAware {
 
         liContainer = new WebMarkupContainer(getLIContainerId("topology"));
         body.add(liContainer);
-        link = BookmarkablePageLinkBuilder.build("topology", Topology.class);
+        if (SyncopeConsoleApplication.get().getDefaultTopologyClass().contains("TabularTopology")) {
+            link = BookmarkablePageLinkBuilder.build("topology", TabularTopology.class);
+        } else {
+            link = BookmarkablePageLinkBuilder.build("topology", Topology.class);
+        }
         MetaDataRoleAuthorizationStrategy.authorize(link, WebPage.RENDER,
                 String.format("%s,%s",
                         StandardEntitlement.CONNECTOR_LIST,
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Connectors.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Connectors.java
new file mode 100644
index 0000000..cf906f6
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Connectors.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.panels.ConnectorDirectoryPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.wicket.PageReference;
+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.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import java.io.Serializable;
+
+public class Connectors extends Panel {
+
+    private static final long serialVersionUID = 305521359617401936L;
+
+    private final WizardMgtPanel<Serializable> connectorDirectoryPanel;
+
+    public Connectors(final String id, final PageReference pageRef) {
+        super(id);
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        add(content);
+
+        Model<String> keywordModel = new Model<>(StringUtils.EMPTY);
+
+        WebMarkupContainer searchBoxContainer = new WebMarkupContainer("searchBox");
+        content.add(searchBoxContainer);
+
+        Form<?> form = new Form<>("form");
+        searchBoxContainer.add(form);
+
+        AjaxTextFieldPanel filter = new AjaxTextFieldPanel("filter", "filter", keywordModel, true);
+        form.add(filter.hideLabel().setOutputMarkupId(true));
+
+        AjaxButton search = new AjaxButton("search") {
+
+            private static final long serialVersionUID = 8390605330558248736L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target) {
+                send(Connectors.this, Broadcast.DEPTH,
+                        new ConnectorDirectoryPanel.ConnectorSearchEvent(target, keywordModel.getObject()));
+            }
+        };
+        search.setOutputMarkupId(true);
+        form.add(search);
+        form.setDefaultButton(search);
+
+        connectorDirectoryPanel =
+                new ConnectorDirectoryPanel.Builder(pageRef).
+                        addNewItemPanelBuilder(new ConnectorWizardBuilder(
+                                new ConnInstanceTO(), pageRef), true).
+                        build("connectorDirectoryPanel");
+        connectorDirectoryPanel.setOutputMarkupId(true);
+
+        content.add(connectorDirectoryPanel);
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Resources.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Resources.java
new file mode 100644
index 0000000..df26dc3
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Resources.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.panels.ResourceDirectoryPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.wicket.PageReference;
+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.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import java.io.Serializable;
+
+public class Resources extends Panel {
+
+    private static final long serialVersionUID = 7240865652350993779L;
+
+    private final WizardMgtPanel<Serializable> resourceDirectoryPanel;
+
+    public Resources(final String id, final PageReference pageRef) {
+        super(id);
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        add(content);
+
+        Model<String> keywordModel = new Model<>(StringUtils.EMPTY);
+
+        WebMarkupContainer searchBoxContainer = new WebMarkupContainer("searchBox");
+        content.add(searchBoxContainer);
+
+        Form<?> form = new Form<>("form");
+        searchBoxContainer.add(form);
+
+        AjaxTextFieldPanel filter = new AjaxTextFieldPanel("filter", "filter", keywordModel, true);
+        form.add(filter.hideLabel().setOutputMarkupId(true));
+
+        AjaxButton search = new AjaxButton("search") {
+
+            private static final long serialVersionUID = 8390605330558248736L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target) {
+                send(Resources.this, Broadcast.DEPTH,
+                        new ResourceDirectoryPanel.ResourceSearchEvent(target, keywordModel.getObject()));
+            }
+        };
+        search.setOutputMarkupId(true);
+        form.add(search);
+        form.setDefaultButton(search);
+
+        resourceDirectoryPanel =
+                new ResourceDirectoryPanel.Builder(pageRef).
+                        addNewItemPanelBuilder(new ResourceWizardBuilder(
+                                new ResourceTO(), pageRef), true).
+                        build("resourceDirectoryPanel");
+        resourceDirectoryPanel.setOutputMarkupId(true);
+
+        content.add(resourceDirectoryPanel);
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java
new file mode 100644
index 0000000..f5420a1
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.java
@@ -0,0 +1,283 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.audit.AuditHistoryModal;
+import org.apache.syncope.client.console.commons.ConnectorDataProvider;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
+import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ConnectorDirectoryPanel extends
+        DirectoryPanel<Serializable, Serializable, ConnectorDataProvider, ConnectorRestClient> {
+
+    private static final long serialVersionUID = 2041468935602350821L;
+
+    private String keyword;
+
+    protected ConnectorDirectoryPanel(final String id, final ConnectorDirectoryPanel.Builder builder) {
+        super(id, builder);
+
+        if (SyncopeConsoleSession.get().owns("CONNECTOR_CREATE")) {
+            MetaDataRoleAuthorizationStrategy.authorizeAll(addAjaxLink, RENDER);
+        } else {
+            MetaDataRoleAuthorizationStrategy.unauthorizeAll(addAjaxLink, RENDER);
+        }
+
+        setShowResultPage(false);
+        modal.size(Modal.Size.Large);
+        initResultTable();
+
+        restClient = builder.restClient;
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof ConnectorSearchEvent) {
+            ConnectorSearchEvent payload = (ConnectorSearchEvent) event.getPayload();
+            AjaxRequestTarget target = payload.getTarget();
+            if (StringUtils.isNotBlank(payload.getKeyword())) {
+                keyword = payload.getKeyword().toLowerCase();
+            }
+            updateResultTable(target);
+        } else {
+            super.onEvent(event);
+        }
+    }
+
+    @Override
+    protected ConnectorDataProvider dataProvider() {
+        dataProvider = new ConnectorDataProvider(rows, pageRef, keyword);
+        return dataProvider;
+    }
+
+    public ConnectorDataProvider getDataProvider() {
+        return dataProvider;
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_PARAMETERS_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected List<IColumn<Serializable, String>> getColumns() {
+        final List<IColumn<Serializable, String>> columns = new ArrayList<>();
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("displayName"), "displayNameSortParam", "displayName"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("connectorName"), "connectorNameSortParam", "connectorName"));
+        return columns;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Collections.singletonList(ActionLink.ActionType.DELETE);
+    }
+
+    @Override
+    public ActionsPanel<Serializable> getActions(final IModel<Serializable> model) {
+        final ActionsPanel<Serializable> panel = super.getActions(model);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 8345646188740279483L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                final ResourceTO modelObject = new ResourceTO();
+                modelObject.setConnector(((ConnInstanceTO) model.getObject()).getKey());
+                modelObject.setConnectorDisplayName(((ConnInstanceTO) model.getObject()).getDisplayName());
+
+                final IModel<ResourceTO> model = new CompoundPropertyModel<>(modelObject);
+                modal.setFormModel(model.getObject());
+
+                target.add(modal.setContent(new ResourceWizardBuilder(modelObject, pageRef).
+                        build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
+
+                modal.header(new Model<>(MessageFormat.format(getString("resource.new"),
+                        model.getObject().getKey())));
+                modal.show(true);
+
+                target.add(modal);
+            }
+
+        }, ActionLink.ActionType.CREATE_RESOURCE, String.format("%s", StandardEntitlement.RESOURCE_CREATE));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 8200500789152854321L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ConnInstanceTO connInstance = restClient.read(((ConnInstanceTO) model.getObject()).getKey());
+
+                final IModel<ConnInstanceTO> model = new CompoundPropertyModel<>(connInstance);
+                modal.setFormModel(model);
+
+                target.add(modal.setContent(new ConnectorWizardBuilder(connInstance, pageRef).
+                        build(BaseModal.CONTENT_ID,
+                                SyncopeConsoleSession.get().
+                                        owns(StandardEntitlement.CONNECTOR_UPDATE, connInstance.getAdminRealm())
+                                        ? AjaxWizard.Mode.EDIT
+                                        : AjaxWizard.Mode.READONLY)));
+
+                modal.header(
+                        new Model<>(MessageFormat.format(getString("connector.edit"), connInstance.getDisplayName())));
+                modal.show(true);
+
+            }
+        }, ActionLink.ActionType.EDIT, String.format("%s,%s", StandardEntitlement.CONNECTOR_READ,
+                StandardEntitlement.CONNECTOR_UPDATE));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 1085863437941911947L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+              ConnInstanceTO modelObject = restClient.read(((ConnInstanceTO) model.getObject()).getKey());
+            
+              target.add(altDefaultModal.setContent(new AuditHistoryModal<ConnInstanceTO>(
+                      altDefaultModal,
+                      AuditElements.EventCategoryType.LOGIC,
+                      "ConnectorLogic",
+                      modelObject,
+                      StandardEntitlement.CONNECTOR_UPDATE,
+                      pageRef) {
+            
+                  private static final long serialVersionUID = -3225348282675513648L;
+
+                  @Override
+                  protected void restore(final String json, final AjaxRequestTarget target) {
+                      try {
+                          ConnInstanceTO updated = MAPPER.readValue(json, ConnInstanceTO.class);
+                          restClient.update(updated);
+
+                          SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                      } catch (Exception e) {
+                          LOG.error("While restoring connector {}",
+                                  ((ConnInstanceTO) model.getObject()).getKey(), e);
+                          SyncopeConsoleSession.get().onException(e);
+                      }
+                      ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                  }
+              }));
+
+              altDefaultModal.header(
+                      new Model<>(MessageFormat.format(getString("connector.menu.history"),
+                              ((ConnInstanceTO) model.getObject()).getDisplayName())));
+
+              altDefaultModal.show(true);
+            }
+
+            }, ActionLink.ActionType.VIEW_AUDIT_HISTORY,
+            String.format("%s,%s", StandardEntitlement.CONNECTOR_READ, StandardEntitlement.AUDIT_LIST));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -1544718936080799146L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                try {
+                    restClient.delete(((ConnInstanceTO) model.getObject()).getKey());
+                    target.appendJavaScript(String.format("jsPlumb.remove('%s');",
+                            ((ConnInstanceTO) model.getObject()).getKey()));
+                    SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting resource {}", ((ConnInstanceTO) model.getObject()).getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.CONNECTOR_DELETE, true);
+
+        return panel;
+    }
+
+    public static class ConnectorSearchEvent implements Serializable {
+
+        private static final long serialVersionUID = -282052400565266028L;
+
+        private final AjaxRequestTarget target;
+
+        private final String keyword;
+
+        public ConnectorSearchEvent(final AjaxRequestTarget target, final String keyword) {
+            this.target = target;
+            this.keyword = keyword;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+    }
+
+    public static class Builder extends DirectoryPanel.Builder<Serializable, Serializable, ConnectorRestClient> {
+
+        private static final long serialVersionUID = 6128427903964630093L;
+
+        public Builder(final PageReference pageRef) {
+            super(new ConnectorRestClient(), pageRef);
+            setShowResultPage(false);
+        }
+
+        @Override
+        protected WizardMgtPanel<Serializable> newInstance(final String id, final boolean wizardInModal) {
+            return new ConnectorDirectoryPanel(id, this);
+        }
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java
new file mode 100644
index 0000000..5ed0e66
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnidLocations.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.DirectoryDataProvider;
+import org.apache.syncope.client.console.rest.RestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.resources.ConnectorWizardBuilder;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+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.markup.html.basic.Label;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class ConnidLocations extends
+        DirectoryPanel<Serializable, Serializable, ConnidLocations.ConnidLocationsDataProvider, RestClient> {
+    
+    public ConnidLocations(final String id, final Builder builder) {
+        super(id, builder);
+
+        disableCheckBoxes();
+        setShowResultPage(true);
+
+        modal.size(Modal.Size.Large);
+        initResultTable();
+    }
+
+    @Override
+    protected ConnidLocationsDataProvider dataProvider() {
+        return new ConnidLocationsDataProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_DYNREALM_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected List<IColumn<Serializable, String>> getColumns() {
+        final List<IColumn<Serializable, String>> columns = new ArrayList<>();
+
+        columns.add(new AbstractColumn<Serializable, String>(
+                new ResourceModel(Constants.KEY_FIELD_NAME), Constants.KEY_FIELD_NAME) {
+            @Override
+            public void populateItem(final Item cellItem, final String componentId, final IModel rowModel) {
+                cellItem.add(new Label(componentId, rowModel.getObject().toString()));
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    public ActionsPanel<Serializable> getActions(final IModel<Serializable> model) {
+        final ActionsPanel<Serializable> panel = super.getActions(model);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 293293495682202660L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                final ConnInstanceTO modelObject = new ConnInstanceTO();
+                modelObject.setLocation((String) ignore);
+
+                final IModel<ConnInstanceTO> model = new CompoundPropertyModel<>(modelObject);
+                modal.setFormModel(model);
+
+                target.add(modal.setContent(new ConnectorWizardBuilder(modelObject, pageRef).
+                        build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
+
+                modal.header(new Model<>(MessageFormat.format(getString("connector.new"), (String) ignore)));
+                modal.show(true);
+            }
+
+        }, ActionLink.ActionType.CREATE, String.format("%s", StandardEntitlement.CONNECTOR_CREATE));
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Collections.emptyList();
+    }
+
+    public abstract static class Builder
+            extends DirectoryPanel.Builder<Serializable, Serializable, RestClient> {
+
+        private static final long serialVersionUID = 4448348557808690524L;
+
+        public Builder(final PageReference pageRef) {
+            super(null, pageRef);
+        }
+
+        @Override
+        protected WizardMgtPanel<Serializable> newInstance(final String id, final boolean wizardInModal) {
+            return new ConnidLocations(id, this);
+        }
+    }
+
+    protected class ConnidLocationsDataProvider extends DirectoryDataProvider<Serializable> {
+
+        private static final long serialVersionUID = 3161906945317209169L;
+
+        public ConnidLocationsDataProvider(final int paginatorRows) {
+            super(paginatorRows);
+        }
+
+        @Override
+        public Iterator<String> iterator(final long first, final long count) {
+            List<String> result = new ArrayList<>(SyncopeConsoleSession.get().getPlatformInfo().getConnIdLocations());
+            return result.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return SyncopeConsoleSession.get().getPlatformInfo().getConnIdLocations().size();
+        }
+
+        @Override
+        public IModel<Serializable> model(final Serializable object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java
new file mode 100644
index 0000000..b52d995
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java
@@ -0,0 +1,432 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.audit.AuditHistoryModal;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.ResourceDataProvider;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.rest.ResourceRestClient;
+import org.apache.syncope.client.console.status.ResourceStatusModal;
+import org.apache.syncope.client.console.tasks.PropagationTasks;
+import org.apache.syncope.client.console.tasks.PullTasks;
+import org.apache.syncope.client.console.tasks.PushTasks;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.console.wizards.AjaxWizard;
+import org.apache.syncope.client.console.wizards.WizardMgtPanel;
+import org.apache.syncope.client.console.wizards.resources.ResourceProvisionPanel;
+import org.apache.syncope.client.console.wizards.resources.ResourceWizardBuilder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class ResourceDirectoryPanel extends
+        DirectoryPanel<Serializable, Serializable, ResourceDataProvider, ResourceRestClient> {
+
+    private static final long serialVersionUID = -5223129956783782225L;
+
+    private String keyword;
+
+    private final ConnectorRestClient connectorRestClient = new ConnectorRestClient();
+
+    private final BaseModal<Serializable> propTaskModal;
+
+    private final BaseModal<Serializable> schedTaskModal;
+
+    private final BaseModal<Serializable> provisionModal;
+
+    private final BaseModal<Serializable> historyModal;
+
+    protected ResourceDirectoryPanel(final String id, final ResourceDirectoryPanel.Builder builder) {
+        super(id, builder);
+
+        if (SyncopeConsoleSession.get().owns("RESOURCE_CREATE")) {
+            MetaDataRoleAuthorizationStrategy.authorizeAll(addAjaxLink, RENDER);
+        } else {
+            MetaDataRoleAuthorizationStrategy.unauthorizeAll(addAjaxLink, RENDER);
+        }
+
+        setShowResultPage(false);
+        modal.size(Modal.Size.Large);
+        initResultTable();
+
+        restClient = builder.restClient;
+
+        propTaskModal = new BaseModal<>(Constants.OUTER);
+        propTaskModal.size(Modal.Size.Large);
+        addOuterObject(propTaskModal);
+
+        schedTaskModal = new BaseModal<Serializable>(Constants.OUTER) {
+
+            private static final long serialVersionUID = -6165152045136958913L;
+
+            @Override
+            protected void onConfigure() {
+                super.onConfigure();
+                setFooterVisible(false);
+            }
+        };
+        schedTaskModal.size(Modal.Size.Large);
+        addOuterObject(schedTaskModal);
+
+        provisionModal = new BaseModal<>(Constants.OUTER);
+        provisionModal.size(Modal.Size.Large);
+        provisionModal.addSubmitButton();
+        addOuterObject(provisionModal);
+
+        historyModal = new BaseModal<>(Constants.OUTER);
+        historyModal.size(Modal.Size.Large);
+        addOuterObject(historyModal);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof ResourceSearchEvent) {
+            ResourceSearchEvent payload = (ResourceSearchEvent) event.getPayload();
+            AjaxRequestTarget target = payload.getTarget();
+            if (StringUtils.isNotEmpty(payload.getKeyword())) {
+                keyword = payload.getKeyword().toLowerCase();
+            }
+            updateResultTable(target);
+        } else {
+            super.onEvent(event);
+        }
+    }
+
+    @Override
+    protected ResourceDataProvider dataProvider() {
+        dataProvider = new ResourceDataProvider(rows, pageRef, keyword);
+        return dataProvider;
+    }
+
+    public ResourceDataProvider getDataProvider() {
+        return dataProvider;
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return Constants.PREF_PARAMETERS_PAGINATOR_ROWS;
+    }
+
+    @Override
+    protected List<IColumn<Serializable, String>> getColumns() {
+        final List<IColumn<Serializable, String>> columns = new ArrayList<>();
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("key"), "keySortParam", "key"));
+        columns.add(new PropertyColumn<>(
+                new ResourceModel("connectorDisplayName"), "connectorDisplayNameSortParam", "connectorDisplayName"));
+        return columns;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Collections.singletonList(ActionLink.ActionType.DELETE);
+    }
+
+    @Override
+    public ActionsPanel<Serializable> getActions(final IModel<Serializable> model) {
+        final ActionsPanel<Serializable> panel = super.getActions(model);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -7220222653598674870L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey());
+                ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector());
+
+                IModel<ResourceTO> model = new CompoundPropertyModel<>(resource);
+                modal.setFormModel(model);
+
+                target.add(modal.setContent(new ResourceWizardBuilder(resource, pageRef).
+                        build(BaseModal.CONTENT_ID,
+                                SyncopeConsoleSession.get().
+                                        owns(StandardEntitlement.RESOURCE_UPDATE, connInstance.getAdminRealm())
+                                        ? AjaxWizard.Mode.EDIT
+                                        : AjaxWizard.Mode.READONLY)));
+
+                modal.header(new Model<>(MessageFormat.format(getString("resource.edit"), model.getObject().getKey())));
+                modal.show(true);
+            }
+        }, ActionLink.ActionType.EDIT, String.format("%s,%s", StandardEntitlement.RESOURCE_READ,
+                StandardEntitlement.RESOURCE_UPDATE));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -6467344504797047254L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey());
+                ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector());
+
+                if (SyncopeConsoleSession.get().
+                        owns(StandardEntitlement.RESOURCE_UPDATE, connInstance.getAdminRealm())) {
+
+                    provisionModal.addSubmitButton();
+                } else {
+                    provisionModal.removeSubmitButton();
+                }
+
+                IModel<ResourceTO> model = new CompoundPropertyModel<>(resource);
+                provisionModal.setFormModel(model);
+
+                target.add(provisionModal.setContent(
+                        new ResourceProvisionPanel(provisionModal, resource, connInstance.getAdminRealm(), pageRef)));
+
+                provisionModal.header(new Model<>(MessageFormat.format(getString("resource.edit"),
+                        model.getObject().getKey())));
+                provisionModal.show(true);
+            }
+        }, ActionLink.ActionType.MAPPING, String.format("%s,%s", StandardEntitlement.RESOURCE_READ,
+                StandardEntitlement.RESOURCE_UPDATE));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -1448897313753684142L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey());
+
+                target.add(propTaskModal.setContent(new ConnObjects(resource, pageRef)));
+                propTaskModal.header(new StringResourceModel("resource.explore.list", Model.of(model.getObject())));
+                propTaskModal.show(true);
+            }
+        }, ActionLink.ActionType.EXPLORE_RESOURCE, StandardEntitlement.RESOURCE_LIST_CONNOBJECT);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 4800323783814856195L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                target.add(propTaskModal.setContent(
+                        new PropagationTasks(propTaskModal, ((ResourceTO) model.getObject()).getKey(), pageRef)));
+                propTaskModal.header(new Model<>(MessageFormat.format(getString("task.propagation.list"),
+                        ((ResourceTO) model.getObject()).getKey())));
+                propTaskModal.show(true);
+            }
+        }, ActionLink.ActionType.PROPAGATION_TASKS, StandardEntitlement.TASK_LIST);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -4699610013584898667L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                target.add(schedTaskModal.setContent(new PullTasks(schedTaskModal, pageRef,
+                        ((ResourceTO) model.getObject()).getKey())));
+                schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.pull.list"),
+                        ((ResourceTO) model.getObject()).getKey())));
+                schedTaskModal.show(true);
+            }
+        }, ActionLink.ActionType.PULL_TASKS, StandardEntitlement.TASK_LIST);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 2042227976628604686L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                target.add(schedTaskModal.setContent(new PushTasks(schedTaskModal, pageRef,
+                        ((ResourceTO) model.getObject()).getKey())));
+                schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.push.list"),
+                        ((ResourceTO) model.getObject()).getKey())));
+                schedTaskModal.show(true);
+            }
+        }, ActionLink.ActionType.PUSH_TASKS, StandardEntitlement.TASK_LIST);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -5962061673680621813L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ResourceTO modelObject = restClient.read(((ResourceTO) model.getObject()).getKey());
+                target.add(propTaskModal.setContent(
+                        new ResourceStatusModal(propTaskModal, pageRef, modelObject)));
+                propTaskModal.header(new Model<>(MessageFormat.format(getString("resource.reconciliation"),
+                        ((ResourceTO) model.getObject()).getKey())));
+                propTaskModal.show(true);
+            }
+        }, ActionLink.ActionType.RECONCILIATION_RESOURCE, StandardEntitlement.USER_UPDATE);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = -5432034353017728766L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                ResourceTO modelObject = restClient.read(((ResourceTO) model.getObject()).getKey());
+
+                target.add(historyModal.setContent(new AuditHistoryModal<ResourceTO>(
+                        historyModal,
+                        AuditElements.EventCategoryType.LOGIC,
+                        "ResourceLogic",
+                        modelObject,
+                        StandardEntitlement.RESOURCE_UPDATE,
+                        pageRef) {
+
+                    private static final long serialVersionUID = -3712506022627033811L;
+
+                    @Override
+                    protected void restore(final String json, final AjaxRequestTarget target) {
+                        try {
+                            ResourceTO updated = MAPPER.readValue(json, ResourceTO.class);
+                            restClient.update(updated);
+
+                            SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                        } catch (Exception e) {
+                            LOG.error("While restoring resource {}", ((ResourceTO) model.getObject()).getKey(), e);
+                            SyncopeConsoleSession.get().onException(e);
+                        }
+                        ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                    }
+                }));
+
+                historyModal.header(
+                        new Model<>(MessageFormat.format(getString("resource.menu.history"),
+                                ((ResourceTO) model.getObject()).getKey())));
+
+                historyModal.show(true);
+            }
+        }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, String.format("%s,%s", StandardEntitlement.RESOURCE_READ,
+                StandardEntitlement.AUDIT_LIST));
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 7019899256702149874L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                try {
+                    ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey());
+                    resource.setKey("Copy of " + resource.getKey());
+                    // reset some resource objects keys
+                    if (resource.getOrgUnit() != null) {
+                        resource.getOrgUnit().setKey(null);
+                        resource.getOrgUnit().getItems().forEach(item -> item.setKey(null));
+                    }
+                    resource.getProvisions().forEach(provision -> {
+                        provision.setKey(null);
+                        if (provision.getMapping() != null) {
+                            provision.getMapping().getItems().forEach(item -> item.setKey(null));
+                            provision.getMapping().getLinkingItems().clear();
+                        }
+                        provision.getVirSchemas().clear();
+                    });
+                    target.add(modal.setContent(new ResourceWizardBuilder(resource, pageRef).
+                            build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE)));
+
+                    modal.header(new Model<>(MessageFormat.format(getString("resource.clone"), resource.getKey())));
+                    modal.show(true);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While cloning resource {}", ((ResourceTO) model.getObject()).getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.CLONE, StandardEntitlement.RESOURCE_CREATE);
+
+        panel.add(new ActionLink<Serializable>() {
+
+            private static final long serialVersionUID = 4516186028545701573L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                try {
+                    restClient.delete(((ResourceTO) model.getObject()).getKey());
+                    target.appendJavaScript(String.format("jsPlumb.remove('%s');",
+                            ((ResourceTO) model.getObject()).getKey()));
+                    SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting resource {}", ((ResourceTO) model.getObject()).getKey(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StandardEntitlement.RESOURCE_DELETE, true);
+
+        return panel;
+    }
+
+    public static class ResourceSearchEvent implements Serializable {
+
+        private static final long serialVersionUID = 213974502541311941L;
+
+        private final AjaxRequestTarget target;
+
+        private final String keyword;
+
+        public ResourceSearchEvent(final AjaxRequestTarget target, final String keyword) {
+            this.target = target;
+            this.keyword = keyword;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+
+        public String getKeyword() {
+            return keyword;
+        }
+    }
+    
+    public static class Builder extends DirectoryPanel.Builder<Serializable, Serializable, ResourceRestClient> {
+
+        private static final long serialVersionUID = -1391308721262593468L;
+
+        public Builder(final PageReference pageRef) {
+            super(new ResourceRestClient(), pageRef);
+            setShowResultPage(false);
+        }
+
+        @Override
+        protected WizardMgtPanel<Serializable> newInstance(final String id, final boolean wizardInModal) {
+            return new ResourceDirectoryPanel(id, this);
+        }
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
index 4a0d64e..e629141 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java
@@ -143,6 +143,119 @@ public abstract class SchedTaskDirectoryPanel<T extends SchedTaskTO>
         addInnerObject(templates);
     }
 
+    protected SchedTaskDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final TaskType taskType,
+            final Class<T> reference,
+            final PageReference pageRef,
+            final boolean wizardInModal) {
+
+        super(baseModal, multiLevelPanelRef, pageRef, wizardInModal);
+        this.taskType = taskType;
+        this.reference = reference;
+
+        try {
+            schedTaskTO = reference.getDeclaredConstructor().newInstance();
+        } catch (Exception e) {
+            LOG.error("Failure instantiating task", e);
+        }
+
+        this.addNewItemPanelBuilder(new SchedTaskWizardBuilder<>(taskType, schedTaskTO, pageRef), true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.TASK_CREATE);
+
+        enableExitButton();
+        setFooterVisibility(false);
+
+        initResultTable();
+
+        container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+            private static final long serialVersionUID = -4661303265651934868L;
+
+            @Override
+            protected void onTimer(final AjaxRequestTarget target) {
+                container.modelChanged();
+                target.add(container);
+            }
+        });
+
+        startAt = new TaskStartAtTogglePanel(container, pageRef);
+        addInnerObject(startAt);
+
+        templates = new TemplatesTogglePanel(getActualId(), this, pageRef) {
+
+            private static final long serialVersionUID = -8765794727538618705L;
+
+            @Override
+            protected Serializable onApplyInternal(
+                    final TemplatableTO targetObject, final String type, final AnyTO anyTO) {
+
+                targetObject.getTemplates().put(type, anyTO);
+                new TaskRestClient().update(taskType, SchedTaskTO.class.cast(targetObject));
+                return targetObject;
+            }
+        };
+        addInnerObject(templates);
+    }
+
+    protected SchedTaskDirectoryPanel(
+            final BaseModal<?> baseModal,
+            final MultilevelPanel multiLevelPanelRef,
+            final TaskType taskType,
+            final Class<T> reference,
+            final PageReference pageRef,
+            final String id) {
+
+        super(baseModal, multiLevelPanelRef, pageRef, id);
+        this.taskType = taskType;
+        this.reference = reference;
+
+        try {
+            schedTaskTO = reference.getDeclaredConstructor().newInstance();
+        } catch (Exception e) {
+            LOG.error("Failure instantiating task", e);
+        }
+
+        this.addNewItemPanelBuilder(new SchedTaskWizardBuilder<>(taskType, schedTaskTO, pageRef), true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.TASK_CREATE);
+
+        setFooterVisibility(false);
+
+        initResultTable();
+
+        container.add(new IndicatorAjaxTimerBehavior(Duration.seconds(10)) {
+
+            private static final long serialVersionUID = 3780176123730676296L;
+
+            @Override
+            protected void onTimer(final AjaxRequestTarget target) {
+                container.modelChanged();
+                target.add(container);
+            }
+        });
+
+        startAt = new TaskStartAtTogglePanel(container, pageRef);
+        addOrReplaceInnerObject(startAt);
+
+        templates = new TemplatesTogglePanel(getActualId(), this, pageRef) {
+
+            private static final long serialVersionUID = 5463425091347504892L;
+
+            @Override
+            protected Serializable onApplyInternal(
+                    final TemplatableTO targetObject, final String type, final AnyTO anyTO) {
+
+                targetObject.getTemplates().put(type, anyTO);
+                new TaskRestClient().update(taskType, SchedTaskTO.class.cast(targetObject));
+                return targetObject;
+            }
+        };
+        addOrReplaceInnerObject(templates);
+    }
+
     protected List<IColumn<T, String>> getFieldColumns() {
         List<IColumn<T, String>> columns = new ArrayList<>();
 
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTasks.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTasks.java
index 562d632..f173c65 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTasks.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTasks.java
@@ -52,4 +52,25 @@ public class SchedTasks extends AbstractTasks {
             }
         });
     }
+
+    public <T extends AnyTO> SchedTasks(final BaseModal<?> baseModal, final PageReference pageReference,
+                                        final boolean wizardInModal, final String id) {
+        super(id);
+
+        final MultilevelPanel mlp = new MultilevelPanel("tasks");
+        add(mlp);
+
+        mlp.setFirstLevel(new SchedTaskDirectoryPanel<SchedTaskTO>(
+                baseModal, mlp, TaskType.SCHEDULED, SchedTaskTO.class, pageReference, wizardInModal) {
+
+            private static final long serialVersionUID = -2195387360323687302L;
+
+            @Override
+            protected void viewTask(final SchedTaskTO taskTO, final AjaxRequestTarget target) {
+                mlp.next(
+                        new StringResourceModel("task.view", this, new Model<>(Pair.of(null, taskTO))).getObject(),
+                        new TaskExecutionDetails<>(baseModal, taskTO, pageReference), target);
+            }
+        });
+    }
 }
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java
index 6cf023f..e9d79b0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/tasks/TaskDirectoryPanel.java
@@ -59,6 +59,26 @@ public abstract class TaskDirectoryPanel<T extends TaskTO>
         setShowResultPage(false);
     }
 
+    protected TaskDirectoryPanel(
+            final BaseModal<?> baseModal, final MultilevelPanel multiLevelPanelRef, final PageReference pageRef,
+            final boolean wizardInModal) {
+        super(MultilevelPanel.FIRST_LEVEL_ID, pageRef, wizardInModal);
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new TaskRestClient();
+        setShowResultPage(false);
+    }
+
+    protected TaskDirectoryPanel(
+            final BaseModal<?> baseModal, final MultilevelPanel multiLevelPanelRef, final PageReference pageRef,
+            final String id) {
+        super(id, pageRef, false);
+        this.baseModal = baseModal;
+        this.multiLevelPanelRef = multiLevelPanelRef;
+        restClient = new TaskRestClient();
+        setShowResultPage(false);
+    }
+
     @Override
     protected void resultTableCustomChanges(final AjaxDataTablePanel.Builder<T, String> resultTableBuilder) {
         resultTableBuilder.setMultiLevelPanel(baseModal, multiLevelPanelRef);
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.java
new file mode 100644
index 0000000..9ba82ce
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/TabularTopology.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.topology;
+
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import de.agilecoders.wicket.core.markup.html.bootstrap.tabs.AjaxBootstrapTabbedPanel;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BasePage;
+import org.apache.syncope.client.console.pages.Connectors;
+import org.apache.syncope.client.console.pages.Resources;
+import org.apache.syncope.client.console.panels.ConnidLocations;
+import org.apache.syncope.client.console.tasks.SchedTasks;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.extensions.markup.html.tabs.ITab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TabularTopology extends BasePage {
+
+    private static final long serialVersionUID = -4434385801124981824L;
+
+    public TabularTopology() {
+        TopologyWebSocketBehavior websocket = new TopologyWebSocketBehavior();
+        body.add(websocket);
+
+        WebMarkupContainer content = new WebMarkupContainer("content");
+        content.setOutputMarkupId(true);
+        content.add(new AjaxBootstrapTabbedPanel<>("tabbedPanel", buildTabList()));
+        body.add(content);
+    }
+
+    private List<ITab> buildTabList() {
+        final List<ITab> tabs = new ArrayList<>();
+
+        tabs.add(new AbstractTab(new Model<>("CustomTasks")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                BaseModal<Serializable> schedTaskModal = new BaseModal<Serializable>(Constants.OUTER) {
+
+                    private static final long serialVersionUID = -1673561782333149836L;
+
+                    @Override
+                    protected void onConfigure() {
+                        super.onConfigure();
+                        setFooterVisible(false);
+                    }
+                };
+                schedTaskModal.size(Modal.Size.Large);
+                return new SchedTasks(schedTaskModal, getPageReference(), true, panelId);
+            }
+        });
+
+        tabs.add(new AbstractTab(new Model<>("Resources")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new Resources(panelId, getPageReference());
+            }
+        });
+
+        tabs.add(new AbstractTab(new Model<>("Connectors")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new Connectors(panelId, getPageReference());
+            }
+        });
+
+        tabs.add(new AbstractTab(new Model<>("ConnectorServers")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new ConnidLocations.Builder(getPageReference()) {
+
+                    private static final long serialVersionUID = -2555113973787214723L;
+
+                }.build(panelId);
+            }
+        });
+
+        return tabs;
+    }
+}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
index 3854929..80367ac 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java
@@ -50,6 +50,8 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         REMOVE_SYNC_TOKEN("update"),
         CLONE("create"),
         CREATE("create"),
+        CREATE_CONNECTOR("create"),
+        CREATE_RESOURCE("create"),
         TEMPLATE("read"),
         EDIT("read"),
         TYPE_EXTENSIONS("read"),
@@ -93,6 +95,7 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         PROVISION_MEMBERS("update"),
         RECONCILIATION_PUSH("update"),
         RECONCILIATION_PULL("update"),
+        RECONCILIATION_RESOURCE("update"),
         MANAGE_RESOURCES("update"),
         MANAGE_ACCOUNTS("update"),
         MERGE_ACCOUNTS("update"),
@@ -100,6 +103,8 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         MANAGE_GROUPS("update"),
         PROPAGATION_TASKS("read"),
         NOTIFICATION_TASKS("read"),
+        PULL_TASKS("read"),
+        PUSH_TASKS("read"),
         ZOOM_IN("zoomin"),
         ZOOM_OUT("zoomout"),
         VIEW_EXECUTIONS("read"),
@@ -107,7 +112,8 @@ public abstract class ActionLink<T extends Serializable> implements Serializable
         MANAGE_APPROVAL("edit"),
         EDIT_APPROVAL("edit"),
         VIEW_AUDIT_HISTORY("read"),
-        EXTERNAL_EDITOR("externalEditor");
+        EXTERNAL_EDITOR("externalEditor"),
+        EXPLORE_RESOURCE("search");
 
         private final String actionId;
 
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
index a4c5bb2..927b4e0 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/CompletenessWidget.java
@@ -22,13 +22,16 @@ import java.util.Map;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
+import org.apache.syncope.client.console.SyncopeConsoleApplication;
 import org.apache.syncope.client.console.chartjs.ChartJSPanel;
 import org.apache.syncope.client.console.chartjs.Doughnut;
 import org.apache.syncope.client.console.chartjs.DoughnutChartData;
+import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.pages.Notifications;
 import org.apache.syncope.client.console.pages.Policies;
 import org.apache.syncope.client.console.pages.Security;
 import org.apache.syncope.client.console.pages.Types;
+import org.apache.syncope.client.console.topology.TabularTopology;
 import org.apache.syncope.client.console.topology.Topology;
 import org.apache.syncope.common.lib.info.NumbersInfo;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
@@ -48,7 +51,7 @@ public class CompletenessWidget extends BaseWidget {
 
     private final WebMarkupContainer actions;
 
-    private final BookmarkablePageLink<Topology> topology;
+    private final BookmarkablePageLink<? extends BasePage> topology;
 
     private final BookmarkablePageLink<Policies> policies;
 
@@ -76,7 +79,11 @@ public class CompletenessWidget extends BaseWidget {
 
         add(actions);
 
-        topology = BookmarkablePageLinkBuilder.build("topology", Topology.class);
+        if (SyncopeConsoleApplication.get().getDefaultTopologyClass().contains("TabularTopology")) {
+            topology = BookmarkablePageLinkBuilder.build("topology", TabularTopology.class);
+        } else {
+            topology = BookmarkablePageLinkBuilder.build("topology", Topology.class);
+        }
         topology.setOutputMarkupPlaceholderTag(true);
         actions.add(topology);
         MetaDataRoleAuthorizationStrategy.authorize(topology, WebPage.ENABLE,
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java b/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
index 023596b..8787a3f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/widgets/NumberWidget.java
@@ -19,8 +19,10 @@
 package org.apache.syncope.client.console.widgets;
 
 import java.util.List;
+import org.apache.syncope.client.console.SyncopeConsoleApplication;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.topology.TabularTopology;
 import org.apache.wicket.behavior.AttributeAppender;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
@@ -95,7 +97,11 @@ public class NumberWidget extends BaseWidget {
                     responsePage = Realms.class;
                     isAuthorized = SyncopeConsoleSession.get().owns(label + "_SEARCH");
                 } else {
-                    responsePage = Topology.class;
+                    if (SyncopeConsoleApplication.get().getDefaultTopologyClass().contains("TabularTopology")) {
+                        responsePage = TabularTopology.class;
+                    } else {
+                        responsePage = Topology.class;
+                    }
                     isAuthorized = SyncopeConsoleSession.get().owns(StandardEntitlement.CONNECTOR_LIST)
                             && SyncopeConsoleSession.get().owns(StandardEntitlement.RESOURCE_LIST);
                 }
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
index 8376833..09534a8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -465,7 +465,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
          * Adds new item panel builder.
          *
          * @param panelBuilder new item panel builder.
-         * @param newItemDefaultButtonEnabled enable default button to adda new item.
+         * @param newItemDefaultButtonEnabled enable default button to add a new item.
          * @return the current builder.
          */
         public Builder<T> addNewItemPanelBuilder(
@@ -476,7 +476,7 @@ public abstract class WizardMgtPanel<T extends Serializable> extends Panel imple
         }
 
         /**
-         * Adds new item panel builder and enables default button to adda new item.
+         * Adds new item panel builder and enables default button to add a new item.
          *
          * @param notificationPanel new item panel builder.
          * @return the current builder.
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
index d417538..2471b16 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
@@ -22,9 +22,11 @@ import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.commons.RealmsUtils;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
 import org.apache.syncope.client.console.rest.RealmRestClient;
 import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
@@ -81,17 +83,53 @@ public class ConnectorDetailsPanel extends WizardStep {
         displayName.addRequiredLabel();
         add(displayName);
 
-        AjaxTextFieldPanel location = new AjaxTextFieldPanel(
-                "location", "location", new PropertyModel<>(connInstanceTO, "location"), false);
-        location.addRequiredLabel();
-        location.setOutputMarkupId(true);
-        location.setEnabled(false);
-        add(location);
-
         final AjaxDropDownChoicePanel<String> bundleName = new AjaxDropDownChoicePanel<>(
                 "bundleName",
                 "bundleName",
                 new PropertyModel<>(connInstanceTO, "bundleName"), false);
+
+        if (StringUtils.isNotBlank(connInstanceTO.getLocation())) {
+            AjaxTextFieldPanel location = new AjaxTextFieldPanel(
+                    "location", "location", new PropertyModel<>(connInstanceTO, "location"), false);
+            location.addRequiredLabel();
+            location.setOutputMarkupId(true);
+            location.setEnabled(false);
+            add(location);
+        } else {
+            final AjaxDropDownChoicePanel<String> location = new AjaxDropDownChoicePanel<>(
+                    "location", "location", new PropertyModel<>(connInstanceTO, "location"), false);
+            location.setChoices(new ArrayList<>(SyncopeConsoleSession.get().getPlatformInfo().getConnIdLocations()));
+            location.addRequiredLabel();
+            location.setOutputMarkupId(true);
+            location.getField().setOutputMarkupId(true);
+            add(location);
+
+            location.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -5609231641453245929L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    ((DropDownChoice<String>) location.getField()).setNullValid(false);
+                    bundleName.setEnabled(true);
+
+                    ConnectorRestClient connectorRestClient = new ConnectorRestClient();
+                    List<ConnBundleTO> bundles = connectorRestClient.getAllBundles().stream().
+                            filter(object -> object.getLocation().equals(connInstanceTO.getLocation())).
+                            collect(Collectors.toList());
+
+                    List<String> listBundles = getBundles(connInstanceTO, bundles);
+                    if (listBundles.size() == 1) {
+                        connInstanceTO.setBundleName(listBundles.get(0));
+                        bundleName.getField().setModelObject(listBundles.get(0));
+                    }
+                    bundleName.setChoices(listBundles);
+
+                    target.add(bundleName);
+                }
+            });
+        }
+
         ((DropDownChoice<String>) bundleName.getField()).setNullValid(true);
 
         List<String> bundleNames = new ArrayList<>();
@@ -124,7 +162,16 @@ public class ConnectorDetailsPanel extends WizardStep {
                 ((DropDownChoice<String>) bundleName.getField()).setNullValid(false);
                 version.setEnabled(true);
 
-                List<String> versions = getVersions(connInstanceTO, bundles);
+                List<String> versions;
+                if (bundles.isEmpty()) {
+                    ConnectorRestClient connectorRestClient = new ConnectorRestClient();
+                    List<ConnBundleTO> bundles = connectorRestClient.getAllBundles().stream().
+                            filter(object -> object.getLocation().equals(connInstanceTO.getLocation())).
+                            collect(Collectors.toList());
+                    versions = getVersions(connInstanceTO, bundles);
+                } else {
+                    versions = getVersions(connInstanceTO, bundles);
+                }
                 if (versions.size() == 1) {
                     connInstanceTO.setVersion(versions.get(0));
                     version.getField().setModelObject(versions.get(0));
@@ -169,4 +216,9 @@ public class ConnectorDetailsPanel extends WizardStep {
                 && object.getBundleName().equals(connInstanceTO.getBundleName())).
                 map(ConnBundleTO::getVersion).collect(Collectors.toList());
     }
+
+    private List<String> getBundles(final ConnInstanceTO connInstanceTO, final List<ConnBundleTO> bundles) {
+        return bundles.stream().filter(object -> object.getLocation().equals(connInstanceTO.getLocation())).
+                map(ConnBundleTO::getBundleName).collect(Collectors.toList());
+    }
 }
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java
index 7fcd82c..1b07626 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorWizardBuilder.java
@@ -82,7 +82,7 @@ public class ConnectorWizardBuilder extends AbstractResourceWizardBuilder<ConnIn
     }
 
     @Override
-    protected Serializable onApplyInternal(final Serializable modelObject) {
+    protected ConnInstanceTO onApplyInternal(final Serializable modelObject) {
         ConnInstanceTO connInstanceTO = ConnInstanceTO.class.cast(modelObject);
         ConnBundleTO bundleTO = ConnectorWizardBuilder.getBundle(connInstanceTO, bundles);
 
@@ -124,7 +124,16 @@ public class ConnectorWizardBuilder extends AbstractResourceWizardBuilder<ConnIn
     }
 
     protected static ConnBundleTO getBundle(final ConnInstanceTO connInstanceTO, final List<ConnBundleTO> bundles) {
-        return bundles.stream().filter(bundle
+        List<ConnBundleTO> bundlesList;
+        if (bundles.isEmpty()) {
+            ConnectorRestClient connectorRestClient = new ConnectorRestClient();
+            bundlesList = connectorRestClient.getAllBundles().stream().
+                    filter(object -> object.getLocation().equals(connInstanceTO.getLocation())).
+                    collect(Collectors.toList());
+        } else {
+            bundlesList = bundles;
+        }
+        return bundlesList.stream().filter(bundle
                 -> bundle.getBundleName().equals(connInstanceTO.getBundleName())
                 && bundle.getVersion().equals(connInstanceTO.getVersion())).
                 findFirst().orElse(null);
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java
index 8a3aa5f..13ed54d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java
@@ -97,7 +97,7 @@ public abstract class ResourceConnConfPanel extends AbstractConnConfPanel<Resour
                         props.add(property);
                     });
         }
-        if (createFlag || resourceTO.getConfOverride().isEmpty()) {
+        if (resourceTO.getConfOverride().isEmpty()) {
             resourceTO.getConfOverride().clear();
         } else {
             Map<String, ConnConfProperty> valuedProps = new HashMap<>();
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceDetailsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceDetailsPanel.java
index 7a62b89..7665895 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceDetailsPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceDetailsPanel.java
@@ -18,10 +18,14 @@
  */
 package org.apache.syncope.client.console.wizards.resources;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
 import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
@@ -34,6 +38,7 @@ import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.TraceLevel;
 import org.apache.wicket.extensions.wizard.WizardStep;
 import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
@@ -141,10 +146,47 @@ public class ResourceDetailsPanel extends WizardStep {
                 false).
                 setChoices(Arrays.stream(TraceLevel.values()).collect(Collectors.toList())).setNullValid(false));
 
-        container.add(new AjaxTextFieldPanel(
-                "connector",
-                new ResourceModel("connector", "connector").getObject(),
-                new Model<>(resourceTO.getConnectorDisplayName()),
-                false).addRequiredLabel().setEnabled(false));
+        if (resourceTO.getConnector() != null) {
+            container.add(new AjaxTextFieldPanel(
+                    "connector",
+                    new ResourceModel("connector", "connector").getObject(),
+                    new Model<>(resourceTO.getConnectorDisplayName()),
+                    false).addRequiredLabel().setEnabled(false));
+        } else {
+            ConnectorRestClient connectorRestClient = new ConnectorRestClient();
+            final AjaxDropDownChoicePanel<String> connector = new AjaxDropDownChoicePanel<>(
+                    "connector",
+                    new ResourceModel("connector", "connector").getObject(),
+                    new PropertyModel<>(resourceTO, "connector"), false);
+            Map<String, String> connectorsMap = new HashMap<>();
+            connectorRestClient.getAllConnectors().forEach(conn -> connectorsMap.put(conn.getKey(),
+                    conn.getDisplayName()));
+            connector.setChoices(new ArrayList<>(connectorsMap.keySet()));
+            connector.setChoiceRenderer(new IChoiceRenderer<String>() {
+
+                private static final long serialVersionUID = 91313845533448846L;
+
+                private final Map<String, String> valueMap = connectorsMap;
+
+                @Override
+                public String getDisplayValue(final String value) {
+                    return valueMap.get(value) == null ? value : valueMap.get(value);
+                }
+
+                @Override
+                public String getIdValue(final String value, final int i) {
+                    return value;
+                }
+
+                @Override
+                public String getObject(final String id, final IModel<? extends List<? extends String>> choices) {
+                    return id;
+                }
+            });
+            connector.addRequiredLabel();
+            connector.setOutputMarkupId(true);
+            connector.getField().setOutputMarkupId(true);
+            container.add(connector);
+        }
     }
 }
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
index b08e29b..bea83ca 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.console.wizards.resources;
 
 import java.io.Serializable;
+import java.util.Collections;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.rest.ConnectorRestClient;
 import org.apache.syncope.client.console.rest.ResourceRestClient;
@@ -72,8 +73,12 @@ public class ResourceWizardBuilder extends AbstractResourceWizardBuilder<Resourc
             }
 
         });
-        wizardModel.add(new ResourceConnCapabilitiesPanel(
-                resourceTO, connectorRestClient.read(resourceTO.getConnector()).getCapabilities()));
+        if (resourceTO.getConnector() != null) {
+            wizardModel.add(new ResourceConnCapabilitiesPanel(
+                    resourceTO, connectorRestClient.read(resourceTO.getConnector()).getCapabilities()));
+        } else {
+            wizardModel.add(new ResourceConnCapabilitiesPanel(resourceTO, Collections.emptySet()));
+        }
 
         wizardModel.add(new ResourceSecurityPanel(resourceTO));
         return wizardModel;
@@ -81,7 +86,7 @@ public class ResourceWizardBuilder extends AbstractResourceWizardBuilder<Resourc
 
     @Override
     protected ResourceTO onApplyInternal(final Serializable modelObject) {
-        ResourceTO resourceTO = ResourceTO.class.cast(modelObject);
+        ResourceTO resourceTO = (ResourceTO) modelObject;
         if (createFlag) {
             resourceTO = resourceRestClient.create(resourceTO);
         } else {
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Connectors.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Connectors.html
new file mode 100644
index 0000000..ec8232e
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Connectors.html
@@ -0,0 +1,42 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<wicket:panel>
+
+  <section class="content" wicket:id="content">
+    <div class="box">
+      <div class="box-body">
+        <div wicket:id="searchBox">
+          <form wicket:id="form">
+            <div class="input-group margin-bottom">
+              <span wicket:id="filter">[FILTER]</span>
+              <span class="input-group-btn">
+                <button type="button" class="btn btn-default btn-flat" wicket:id="search">
+                  <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
+                </button>
+              </span>
+            </div>
+          </form>
+        </div>
+        <div wicket:id="connectorDirectoryPanel"></div>
+      </div>
+    </div>
+  </section>
+</wicket:panel>
+</html>
\ No newline at end of file
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/pages/Resources.html b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Resources.html
new file mode 100644
index 0000000..fd17ae2
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/pages/Resources.html
@@ -0,0 +1,42 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<wicket:panel>
+
+  <section class="content" wicket:id="content">
+    <div class="box">
+      <div class="box-body">
+        <div wicket:id="searchBox">
+          <form wicket:id="form">
+            <div class="input-group margin-bottom">
+              <span wicket:id="filter">[FILTER]</span>
+              <span class="input-group-btn">
+                <button type="button" class="btn btn-default btn-flat" wicket:id="search">
+                  <span class="glyphicon glyphicon-search" aria-hidden="true"></span>
+                </button>
+              </span>
+            </div>
+          </form>
+        </div>
+        <div wicket:id="resourceDirectoryPanel"></div>
+      </div>
+    </div>
+  </section>
+</wicket:panel>
+</html>
\ No newline at end of file
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.properties
new file mode 100644
index 0000000..feec3b7
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=Connector Name
+connectorName=Connector Type
+
+any.new=New connector
+connector.edit=Edit connector {0}
+connector.menu.add=Add new connector
+connector.menu.remove=Remove connector
+connector.menu.edit=Edit connector
+connector.menu.history=Configuration history
+
+resource.new=New resource
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_fr_CA.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_fr_CA.properties
new file mode 100644
index 0000000..c739b94
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_fr_CA.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=Nom du connecteur
+connectorName=Type de connecteur
+
+any.new=Nouveau connecteur
+connector.edit=Modifier le connecteur {0}
+connector.menu.add=Ajouter un nouveau connecteur
+connector.menu.remove=Retirer le connecteur
+connector.menu.edit=Modifier le connecteur
+connector.menu.history=Historique des configurations
+
+resource.new=Nouvelle ressource
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_it.properties
new file mode 100644
index 0000000..9b3d7c0
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_it.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=Nome Connettore
+connectorName=Tipo Connettore
+
+any.new=Nuovo connettore
+connector.edit=Modifica connettore {0}
+connector.menu.add=Aggiungi nuovo connettore
+connector.menu.remove=Rimuovi connettore
+connector.menu.edit=Modifica connettore
+connector.menu.history=Storico delle configurazioni
+
+resource.new=Nuova risorsa
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ja.properties
new file mode 100644
index 0000000..a2bd6fd
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ja.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=\u30b3\u30cd\u30af\u30bf\u540d
+connectorName=\u30b3\u30cd\u30af\u30bf\u30bf\u30a4\u30d7
+
+any.new=\u65b0\u3057\u3044\u30b3\u30cd\u30af\u30bf
+connector.edit=\u30b3\u30cd\u30af\u30bf\u3092\u7de8\u96c6\u3059\u308b {0}
+connector.menu.add=\u65b0\u3057\u3044\u30b3\u30cd\u30af\u30bf\u3092\u8ffd\u52a0\u3057\u307e\u3059
+connector.menu.remove=\u30b3\u30cd\u30af\u30bf\u3092\u53d6\u308a\u5916\u3057\u307e\u3059
+connector.menu.edit=\u30b3\u30cd\u30af\u30bf\u3092\u7de8\u96c6\u3059\u308b
+connector.menu.history=\u69cb\u6210\u5c65\u6b74
+
+resource.new=\u65b0\u3057\u3044\u30ea\u30bd\u30fc\u30b9
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..307a0bb
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_pt_BR.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=Nome do conector
+connectorName=Tipo de conector
+
+any.new=Novo conector
+connector.edit=Editar conector {0}
+connector.menu.add=Adicionar novo conector
+connector.menu.remove=Remova o conector
+connector.menu.edit=Editar conector
+connector.menu.history=Hist\u00f3rico de Configura\u00e7\u00c3o
+
+resource.new=Novo recurso
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ru.properties
new file mode 100644
index 0000000..ec6f84c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnectorDirectoryPanel_ru.properties
@@ -0,0 +1,27 @@
+# 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.
+displayName=\u0418\u043c\u044f\u0020\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044f
+connectorName=\u0422\u0438\u043f\u0020\u0440\u0430\u0437\u044a\u0435\u043c\u0430
+
+any.new=\u041d\u043e\u0432\u044b\u0439\u0020\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
+connector.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0020\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044c {0}
+connector.menu.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\u0020\u043d\u043e\u0432\u044b\u0439\u0020\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
+connector.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c\u0020\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044c
+connector.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0020\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044c
+connector.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f\u0020\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
+
+resource.new=\u041d\u043e\u0432\u044b\u0439\u0020\u0440\u0435\u0441\u0443\u0440\u0441
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations.properties
new file mode 100644
index 0000000..1fd8e9e
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Connid locations
+connector.new=New connector
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_fr_CA.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_fr_CA.properties
new file mode 100644
index 0000000..6b31536
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_fr_CA.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Emplacements Connid
+connector.new=Nouveau connecteur
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_it.properties
new file mode 100644
index 0000000..c877b7e
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_it.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Connid locations
+connector.new=Nuovo connettore
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ja.properties
new file mode 100644
index 0000000..26559d2
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ja.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Connid \u306e\u5834\u6240
+connector.new=\u65b0\u3057\u3044\u30b3\u30cd\u30af\u30bf
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_pt_BR.properties
new file mode 100644
index 0000000..3b78e3f
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_pt_BR.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Locais de Connid
+connector.new=Novo conector
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ru.properties
new file mode 100644
index 0000000..642498c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ConnidLocations_ru.properties
@@ -0,0 +1,18 @@
+# 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.
+key=Connid \u043b\u043e\u043a\u0430\u0446\u0438\u0438
+connector.new=\u041d\u043e\u0432\u044b\u0439\u0020\u043a\u043e\u043d\u043d\u0435\u043a\u0442\u043e\u0440
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.properties
new file mode 100644
index 0000000..85c330f
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.properties
@@ -0,0 +1,38 @@
+# 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.
+key=Resource Name
+connectorDisplayName=Connector
+
+any.new=New resource
+resource.edit=Edit resource {0}
+resource.menu.add=Add new resource
+resource.menu.remove=Remove resource
+resource.menu.edit=Edit resource
+resource.menu.provision=Edit provision rules
+resource.menu.explore=Explore resource
+resource.menu.history=Configuration history
+resource.menu.clone=Clone resource
+
+task.propagation.list=Propagation tasks {0}
+task.pull.list=Pull tasks {0}
+task.push.list=Push tasks {0}
+resource.explore.list=Explore ${key}
+resource.reconciliation=Reconciliation {0}
+resource.menu.reconciliation=Reconciliation
+resource.menu.push.list=Push tasks
+resource.menu.pull.list=Pull tasks
+resource.menu.propagation.list=Propagation tasks
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_fr_CA.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_fr_CA.properties
new file mode 100644
index 0000000..ab162bb
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_fr_CA.properties
@@ -0,0 +1,38 @@
+# 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.
+key=Nom de la ressource
+connectorDisplayName=Connecteur
+
+any.new=Nouvelle ressource
+resource.edit=Modifier la ressource {0}
+resource.menu.add=Ajouter une nouvelle ressource
+resource.menu.remove=Supprimer la ressource
+resource.menu.edit=Modifier la ressource
+resource.menu.provision=Modifier les r\u00e8gles de mise \u00e0 disposition
+resource.menu.explore=Explorer la ressource
+resource.menu.history=Historique des configurations
+resource.menu.clone=Cloner la ressource
+
+task.propagation.list=T\u00e2ches de propagation {0}
+task.pull.list=T\u00e2ches d\u0027extraction {0}
+task.push.list=Pousser les t\u00e2ches {0}
+resource.explore.list=Explorer ${key}
+resource.reconciliation=R\u00e9conciliation {0}
+resource.menu.reconciliation=R\u00e9conciliation
+resource.menu.push.list=Pousser les t\u00e2ches
+resource.menu.pull.list=T\u00e2ches d\u0027extraction
+resource.menu.propagation.list=T\u00e2ches de propagation
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_it.properties
new file mode 100644
index 0000000..b5e97bc
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_it.properties
@@ -0,0 +1,38 @@
+# 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.
+key=Nome Risorsa
+connectorDisplayName=Connettore
+
+any.new=Nuova risorsa
+resource.edit=Modifica risorsa {0}
+resource.menu.add=Aggiungi nuova risorsa
+resource.menu.remove=Rimuovi risorsa
+resource.menu.edit=Modifica risorsa
+resource.menu.provision=Modifica regole di provisioning
+resource.menu.explore=Esplora risorsa
+resource.menu.history=Storico delle configurazioni
+resource.menu.clone=Duplica risorsa
+
+task.propagation.list=Task di propagazione {0}
+task.pull.list=Pull task {0}
+task.push.list=Push task {0}
+resource.explore.list=Esplora ${key}
+resource.reconciliation=Riconciliazione {0}
+resource.menu.reconciliation=Riconciliazione
+resource.menu.push.list=Push tasks
+resource.menu.pull.list=Pull tasks
+resource.menu.propagation.list=Task di propagazione
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ja.properties
new file mode 100644
index 0000000..e820162
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ja.properties
@@ -0,0 +1,38 @@
+# 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.
+key=\u30ea\u30bd\u30fc\u30b9\u540d
+connectorDisplayName=\u30b3\u30cd\u30af\u30bf
+
+any.new=\u65b0\u3057\u3044\u30ea\u30bd\u30fc\u30b9
+resource.edit=\u30ea\u30bd\u30fc\u30b9\u306e\u7de8\u96c6 {0}
+resource.menu.add=\u65b0\u3057\u3044\u30ea\u30bd\u30fc\u30b9\u3092\u8ffd\u52a0\u3059\u308b
+resource.menu.remove=\u30ea\u30bd\u30fc\u30b9\u3092\u524a\u9664\u3059\u308b
+resource.menu.edit=\u30ea\u30bd\u30fc\u30b9\u306e\u7de8\u96c6
+resource.menu.provision=\u30d7\u30ed\u30d3\u30b8\u30e7\u30cb\u30f3\u30b0\u30eb\u30fc\u30eb\u306e\u7de8\u96c6
+resource.menu.explore=\u30ea\u30bd\u30fc\u30b9\u3092\u63a2\u7d22\u3059\u308b
+resource.menu.history=\u69cb\u6210\u5c65\u6b74
+resource.menu.clone=\u30ea\u30bd\u30fc\u30b9\u306e\u30af\u30ed\u30fc\u30f3
+
+task.propagation.list=\u4f1d\u64ad\u30bf\u30b9\u30af {0}
+task.pull.list=\u30bf\u30b9\u30af\u3092\u30d7\u30eb\u3059\u308b {0}
+task.push.list=\u30d7\u30c3\u30b7\u30e5\u30bf\u30b9\u30af {0}
+resource.explore.list=\u63a2\u691c ${key}
+resource.reconciliation=\u548c\u89e3 {0}
+resource.menu.reconciliation=\u548c\u89e3
+resource.menu.push.list=\u30d7\u30c3\u30b7\u30e5\u30bf\u30b9\u30af
+resource.menu.pull.list=\u30bf\u30b9\u30af\u3092\u30d7\u30eb\u3059\u308b
+resource.menu.propagation.list=\u4f1d\u64ad\u30bf\u30b9\u30af
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_pt_BR.properties
new file mode 100644
index 0000000..1600a2c
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_pt_BR.properties
@@ -0,0 +1,38 @@
+# 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.
+key=Nome do recurso
+connectorDisplayName=Conector
+
+any.new=Novo recurso
+resource.edit=Editar recurso {0}
+resource.menu.add=Adicionar novo recurso
+resource.menu.remove=Remover recurso
+resource.menu.edit=Editar recurso
+resource.menu.provision=Editar regras de provis\u00e3o
+resource.menu.explore=Explorar recurso
+resource.menu.history=Hist\u00f3rico de Configura\u00e7\u00e3o
+resource.menu.clone=Clonar recurso
+
+task.propagation.list=Tarefas de propaga\u00e7\u00e3o {0}
+task.pull.list=Puxe tarefas {0}
+task.push.list=Tarefas push {0}
+resource.explore.list=Explorar ${key}
+resource.reconciliation=Reconcilia\u00e7\u00e3o {0}
+resource.menu.reconciliation=Reconcilia\u00e7\u00e3o
+resource.menu.push.list=Tarefas push
+resource.menu.pull.list=Puxe tarefas
+resource.menu.propagation.list=Tarefas de propaga\u00e7\u00e3o
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ru.properties
new file mode 100644
index 0000000..b712405
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/ResourceDirectoryPanel_ru.properties
@@ -0,0 +1,38 @@
+# 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.
+key=\u0418\u043c\u044f\u0020\u0440\u0435\u0441\u0443\u0440\u0441\u0430
+connectorDisplayName=\u0421\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044c
+
+any.new=\u041d\u043e\u0432\u044b\u0439\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+resource.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441 {0}
+resource.menu.add=\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c\u0020\u043d\u043e\u0432\u044b\u0439\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+resource.menu.remove=\u0423\u0434\u0430\u043b\u0438\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+resource.menu.edit=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+resource.menu.provision=\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c\u0020\u043f\u0440\u0430\u0432\u0438\u043b\u0430\u0020\u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f
+resource.menu.explore=\u0418\u0441\u0441\u043b\u0435\u0434\u0443\u0439\u0442\u0435\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+resource.menu.history=\u0418\u0441\u0442\u043e\u0440\u0438\u044f\u0020\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438
+resource.menu.clone=\u041a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+
+task.propagation.list=\u0417\u0430\u0434\u0430\u0447\u0438\u0020\u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u044f {0}
+task.pull.list=\u0412\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u0439\u0442\u0435\u0020\u0437\u0430\u0434\u0430\u0447\u0438 {0}
+task.push.list=Push-\u0437\u0430\u0434\u0430\u0447\u0438 {0}
+resource.explore.list=\u041f\u0440\u043e\u0432\u043e\u0434\u0438\u0442\u044c\u0020\u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f ${key}
+resource.reconciliation=\u041f\u0440\u0438\u043c\u0438\u0440\u0435\u043d\u0438\u0435 {0}
+resource.menu.reconciliation=\u041f\u0440\u0438\u043c\u0438\u0440\u0435\u043d\u0438\u0435
+resource.menu.push.list=Push-\u0437\u0430\u0434\u0430\u0447\u0438
+resource.menu.pull.list=\u0412\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u0439\u0442\u0435\u0020\u0437\u0430\u0434\u0430\u0447\u0438
+resource.menu.propagation.list=\u0417\u0430\u0434\u0430\u0447\u0438\u0020\u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u044f
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/topology/TabularTopology.html b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TabularTopology.html
new file mode 100644
index 0000000..3e9f9f6
--- /dev/null
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/topology/TabularTopology.html
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<wicket:extend>
+  <section class="content" wicket:id="content">
+    <div class="box">
+      <div class="box-body" wicket:id="tabbedPanel"/>
+    </div>
+  </section>
+</wicket:extend>
+</html>
\ No newline at end of file
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
index 9806974..af9b822 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
@@ -38,6 +38,14 @@ create.class=glyphicon glyphicon-plus
 create.title=create
 create.alt=create icon
 
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title=create connector
+create_connector.alt=create icon
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title=create resource
+create_resource.alt=create icon
+
 template.class=fa fa-list-alt
 template.title=template
 template.alt=template icon
@@ -241,6 +249,10 @@ reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=pull
 reconciliation_pull.alt=reconciliation pull icon
 
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=reconciliation
+reconciliation_resource.alt=reconciliation resource icon
+
 manage_resources.class=fa fa-sitemap
 manage_resources.title=manage resources
 manage_resources.alt=manage resources icon
@@ -261,6 +273,14 @@ notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=notification tasks
 notification_tasks.alt=notification tasks icon
 
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=pull tasks
+pull_tasks.alt=pull tasks icon
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=push tasks
+push_tasks.alt=push tasks icon
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=zoom-in
 zoom_in.alt=zoom-in icon
@@ -285,3 +305,7 @@ merge_accounts.class=fa fa-compress
 merge_accounts.title=merge accounts
 merge_accounts.alt=merge accounts icon
 
+explore_resource.class=fa fa-eye
+explore_resource.title=explore resource
+explore_resource.alt=explore resource icon
+
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
index f1fbdbe..ba1fc85 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
@@ -32,6 +32,15 @@ clone.alt=ic\u00f4ne cl\u00f4ner
 create.class=glyphicon glyphicon-plus
 create.title=cr\u00e9er
 create.alt=ic\u00f4ne cr\u00e9er
+
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title=cr\u00e9er un connecteur
+create_connector.alt=ic\u00f4ne cr\u00e9er
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title=cr\u00e9er une ressource
+create_resource.alt=ic\u00f4ne cr\u00e9er
+
 template.class=fa fa-list-alt
 template.title=mod\u00e8le
 template.alt=ic\u00f4ne mod\u00e8le
@@ -185,6 +194,11 @@ reconciliation_push.alt=ic\u00f4ne push r\u00e9conciliation
 reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=pull
 reconciliation_pull.alt=ic\u00f4ne pull r\u00e9conciliation
+
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=r\u00e9conciliation
+reconciliation_resource.alt=ic\u00f4ne de ressource de r\u00e9conciliation
+
 manage_resources.class=fa fa-sitemap
 manage_resources.title=g\u00e9rer ressources
 manage_resources.alt=ic\u00f4ne g\u00e9rer ressources
@@ -200,6 +214,15 @@ propagation_tasks.alt=ic\u00f4ne t\u00e2ches de propagation
 notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=t\u00e2ches de notification
 notification_tasks.alt=ic\u00f4ne t\u00e2ches de notification
+
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=t\u00c2ches d\u0027extraction
+pull_tasks.alt=ic\u00f4ne t\u00c2ches d\u0027extraction
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=pousser des t\u00c2ches
+push_tasks.alt=ic\u00f4ne pousser des t\u00c2ches
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=zoom-in
 zoom_in.alt=ic\u00f4ne zoom-in
@@ -215,3 +238,7 @@ view_audit_history.alt=manage history
 external_editor.title=external editor icon
 external_editor.class=fa fa-picture-o
 external_editor.alt=external editor icon
+
+explore_resource.class=fa fa-eye
+explore_resource.title=explorer la ressource
+explore_resource.alt=ic\u00f4ne explorer la ressource
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
index 377b547..b9f7554 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
@@ -38,6 +38,14 @@ create.class=glyphicon glyphicon-plus
 create.title=crea
 create.alt=create icon
 
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title=crea connettore
+create_connector.alt=create icon
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title=crea risorsa
+create_resource.alt=create icon
+
 template.class=fa fa-list-alt
 template.title=modello
 template.alt=template icon
@@ -250,6 +258,14 @@ notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=task di notifica
 notification_tasks.alt=notification tasks icon
 
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=pull tasks
+pull_tasks.alt=pull tasks icon
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=push tasks
+push_tasks.alt=push tasks icon
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=ingrandisci
 zoom_in.alt=zoom-in icon
@@ -265,6 +281,10 @@ reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=pull
 reconciliation_pull.alt=reconciliation pull icon
 
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=riconciliazione
+reconciliation_resource.alt=reconciliation resource icon
+
 manage_accounts.class=fa fa-users
 manage_accounts.title=gestisci account
 manage_accounts.alt=manage accounts icon
@@ -281,3 +301,7 @@ external_editor.title=external editor icon
 external_editor.class=fa fa-picture-o
 external_editor.alt=external editor icon
 
+explore_resource.class=fa fa-eye
+explore_resource.title=esplora risorsa
+explore_resource.alt=explore resource icon
+
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
index 4de5ebe..d1aab38 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
@@ -38,6 +38,14 @@ create.class=glyphicon glyphicon-plus
 create.title=\u4f5c\u6210
 create.alt=\u4f5c\u6210
 
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title= \u30b3\u30cd\u30af\u30bf\u3092\u4f5c\u6210\u3059\u308b
+create_connector.alt=\u4f5c\u6210
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title= \u30ea\u30bd\u30fc\u30b9\u3092\u4f5c\u6210\u3059\u308b
+create_resource.alt=\u4f5c\u6210
+
 template.class=fa fa-list-alt
 template.title=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8
 template.alt=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8
@@ -250,6 +258,14 @@ notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=\u901a\u77e5\u30bf\u30b9\u30af
 notification_tasks.alt=\u901a\u77e5\u30bf\u30b9\u30af
 
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=\u30bf\u30b9\u30af\u3092\u30d7\u30eb\u3059\u308b
+pull_tasks.alt=\u30bf\u30b9\u30af\u30a2\u30a4\u30b3\u30f3\u3092\u5f15\u3063\u5f35\u308b
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=\u30d7\u30c3\u30b7\u30e5\u30bf\u30b9\u30af
+push_tasks.alt=\u30bf\u30b9\u30af\u30a2\u30a4\u30b3\u30f3\u3092\u62bc\u3059
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=\u30ba\u30fc\u30e0\u30a4\u30f3
 zoom_in.alt=\u30ba\u30fc\u30e0\u30a4\u30f3
@@ -266,6 +282,10 @@ reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=\u30d7\u30eb
 reconciliation_pull.alt=\u7167\u5408\u30d7\u30eb icon
 
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=\u548c\u89e3
+reconciliation_resource.alt=\u8abf\u6574\u30ea\u30bd\u30fc\u30b9\u30a2\u30a4\u30b3\u30f3
+
 manage_accounts.class=fa fa-users
 manage_accounts.title=\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u7ba1\u7406\u3059\u308b
 manage_accounts.alt=\u30a2\u30ab\u30a6\u30f3\u30c8\u7ba1\u7406\u30a2\u30a4\u30b3\u30f3
@@ -282,3 +302,7 @@ external_editor.title=external editor icon
 external_editor.class=fa fa-picture-o
 external_editor.alt=external editor icon
 
+explore_resource.class=fa fa-eye
+explore_resource.title=\u30ea\u30bd\u30fc\u30b9\u3092\u63a2\u7d22\u3059\u308b
+explore_resource.alt=\u30ea\u30bd\u30fc\u30b9\u30a2\u30a4\u30b3\u30f3\u3092\u63a2\u3059
+
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
index 9a4ffbf..2ef3f17 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
@@ -38,6 +38,14 @@ create.class=glyphicon glyphicon-plus
 create.title=create
 create.alt=create icon
 
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title=criar conector
+create_connector.alt=criar \u00edcone
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title=criar recurso
+create_resource.alt=criar \u00edcone
+
 template.class=fa fa-list-alt
 template.title=template
 template.alt=template icon
@@ -241,6 +249,10 @@ reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=pull
 reconciliation_pull.alt=reconciliation pull icon
 
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=reconcilia\u00e7\u00c3o
+reconciliation_resource.alt=\u00edcone de recurso de reconcilia\u00e7\u00c3o
+
 manage_resources.class=fa fa-sitemap
 manage_resources.title=manage resources
 manage_resources.alt=manage resources icon
@@ -261,6 +273,14 @@ notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=notification tasks
 notification_tasks.alt=notification tasks icon
 
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=puxar tarefas
+pull_tasks.alt=\u00edcone de puxar tarefas
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=tarefas push
+push_tasks.alt=\u00edcone de tarefas push
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=zoom-in
 zoom_in.alt=zoom-in icon
@@ -286,3 +306,7 @@ view_audit_history.alt=manage history icon
 external_editor.title=external editor icon
 external_editor.class=fa fa-picture-o
 external_editor.alt=external editor icon
+
+explore_resource.class=fa fa-eye
+explore_resource.title=explorar recurso
+explore_resource.alt=\u00edcone de explorar recurso
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
index 365367e..64c961c 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
@@ -38,6 +38,14 @@ create.class=glyphicon glyphicon-plus
 create.title=create
 create.alt=create icon
 
+create_connector.class=glyphicon glyphicon-plus
+create_connector.title=\u0441\u043e\u0437\u0434\u0430\u0442\u044c\u0020\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u0435\u043b\u044c
+create_connector.alt=\u0441\u043e\u0437\u0434\u0430\u0442\u044c\u0020\u0437\u043d\u0430\u0447\u043e\u043a
+
+create_resource.class=glyphicon glyphicon-plus
+create_resource.title=\u0441\u043e\u0437\u0434\u0430\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+create_resource.alt=\u0441\u043e\u0437\u0434\u0430\u0442\u044c\u0020\u0437\u043d\u0430\u0447\u043e\u043a
+
 template.class=fa fa-list-alt
 template.title=template
 template.alt=template icon
@@ -250,6 +258,14 @@ notification_tasks.class=fa fa-envelope-o
 notification_tasks.title=notification tasks
 notification_tasks.alt=notification tasks icon
 
+pull_tasks.class=fa fa-chevron-circle-left
+pull_tasks.title=\u0442\u044f\u043d\u0443\u0442\u044c\u0020\u0437\u0430\u0434\u0430\u0447\u0438
+pull_tasks.alt=\u0432\u044b\u0442\u0430\u0449\u0438\u0442\u044c\u0020\u0437\u043d\u0430\u0447\u043e\u043a\u0020\u0437\u0430\u0434\u0430\u0447\u0438
+
+push_tasks.class=fa fa-chevron-circle-right
+push_tasks.title=push-\u0437\u0430\u0434\u0430\u0447\u0438
+push_tasks.alt=\u0437\u043d\u0430\u0447\u043e\u043a push-\u0437\u0430\u0434\u0430\u0447\u0438
+
 zoom_in.class=fa fa-search-plus
 zoom_in.title=zoom-in
 zoom_in.alt=zoom-in icon
@@ -266,6 +282,10 @@ reconciliation_pull.class=fa fa-chevron-circle-left
 reconciliation_pull.title=pull
 reconciliation_pull.alt=reconciliation pull icon
 
+reconciliation_resource.class=fa fa-chevron-circle-left
+reconciliation_resource.title=\u043f\u0440\u0438\u043c\u0438\u0440\u0435\u043d\u0438\u0435
+reconciliation_resource.alt=\u0437\u043d\u0430\u0447\u043e\u043a\u0020\u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0020\u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043d\u0438\u044f
+
 manage_accounts.class=fa fa-users
 manage_accounts.title=manage accounts
 manage_accounts.alt=manage accounts icon
@@ -282,3 +302,7 @@ external_editor.title=external editor icon
 external_editor.class=fa fa-picture-o
 external_editor.alt=external editor icon
 
+explore_resource.class=fa fa-eye
+explore_resource.title=\u0438\u0437\u0443\u0447\u0438\u0442\u044c\u0020\u0440\u0435\u0441\u0443\u0440\u0441
+explore_resource.alt=\u0438\u0437\u0443\u0447\u0438\u0442\u044c\u0020\u0437\u043d\u0430\u0447\u043e\u043a\u0020\u0440\u0435\u0441\u0443\u0440\u0441\u0430
+