You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/02/13 12:44:48 UTC

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfModalPage.java
new file mode 100644
index 0000000..0c65ac2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfModalPage.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import java.util.Collections;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.Mode;
+import org.apache.syncope.client.console.panels.PlainAttrsPanel;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConfTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.ResourceModel;
+
+public class ConfModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = 3524777398688399977L;
+
+    public ConfModalPage(final PageReference pageRef, final ModalWindow window, final WebMarkupContainer parameters) {
+        super();
+
+        MetaDataRoleAuthorizationStrategy.authorize(
+                parameters, ENABLE, xmlRolesReader.getEntitlement("Configuration", "list"));
+        final ConfTO conf = confRestClient.list();
+
+        final Form<ConfTO> form = new Form<>("confForm");
+        form.setModel(new CompoundPropertyModel<>(conf));
+
+        form.add(new PlainAttrsPanel("paramAttrs", conf, form, Mode.ADMIN));
+
+        final AjaxButton submit = new IndicatingAjaxButton(SUBMIT, new ResourceModel(SUBMIT)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                final ConfTO updatedConf = (ConfTO) form.getModelObject();
+
+                try {
+                    for (AttrTO attr : updatedConf.getPlainAttrs()) {
+                        attr.getValues().removeAll(Collections.singleton(null));
+                        if (attr.getValues().isEmpty()
+                                || attr.getValues().equals(Collections.singletonList(StringUtils.EMPTY))) {
+
+                            confRestClient.delete(attr.getSchema());
+                        } else {
+                            confRestClient.set(attr);
+                        }
+                    }
+
+                    if (pageRef.getPage() instanceof BasePage) {
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                    }
+
+                    window.close(target);
+                } catch (Exception e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                    feedbackPanel.refresh(target);
+                }
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                feedbackPanel.refresh(target);
+            }
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(
+                submit, ENABLE, xmlRolesReader.getEntitlement("Configuration", "set"));
+        MetaDataRoleAuthorizationStrategy.authorize(
+                submit, ENABLE, xmlRolesReader.getEntitlement("Configuration", "delete"));
+        form.add(submit);
+        form.setDefaultButton(submit);
+
+        final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+        };
+        cancel.setDefaultFormProcessing(false);
+        form.add(cancel);
+
+        add(form);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
new file mode 100644
index 0000000..c6774f2
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/Configuration.java
@@ -0,0 +1,814 @@
+/*
+ * 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 static org.apache.syncope.client.console.pages.AbstractBasePage.LOG;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.syncope.client.console.ExtensionPanel;
+import org.apache.syncope.client.console.commons.AttrLayoutType;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.HttpResourceStream;
+import org.apache.syncope.client.console.commons.PreferenceManager;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.init.ImplementationClassNamesLoader;
+import org.apache.syncope.client.console.panels.AbstractExtensionPanel;
+import org.apache.syncope.client.console.panels.JQueryUITabbedPanel;
+import org.apache.syncope.client.console.panels.LayoutsPanel;
+import org.apache.syncope.client.console.panels.PoliciesPanel;
+import org.apache.syncope.client.console.rest.LoggerRestClient;
+import org.apache.syncope.client.console.rest.NotificationRestClient;
+import org.apache.syncope.client.console.rest.SecurityQuestionRestClient;
+import org.apache.syncope.client.console.rest.WorkflowRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.CollectionPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.client.console.wicket.markup.html.link.VeilPopupSettings;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.LoggerTO;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.common.lib.types.LoggerLevel;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.ajax.markup.html.repeater.data.table.AjaxFallbackDefaultDataTable;
+import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
+import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.image.Image;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.PropertyListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.markup.repeater.Item;
+import org.apache.wicket.model.AbstractReadOnlyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
+import org.apache.wicket.request.resource.ContentDisposition;
+import org.apache.wicket.request.resource.DynamicImageResource;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Configurations WebPage.
+ */
+public class Configuration extends BasePage {
+
+    private static final long serialVersionUID = -2838270869037702214L;
+
+    private static final int SYNCOPECONF_WIN_HEIGHT = 300;
+
+    private static final int SYNCOPECONF_WIN_WIDTH = 900;
+
+    private static final int NOTIFICATION_WIN_HEIGHT = 500;
+
+    private static final int NOTIFICATION_WIN_WIDTH = 1100;
+
+    private static final int SECURITY_QUESTION_WIN_HEIGHT = 300;
+
+    private static final int SECURITY_QUESTION_WIN_WIDTH = 900;
+
+    @SpringBean
+    private LoggerRestClient loggerRestClient;
+
+    @SpringBean
+    private NotificationRestClient notificationRestClient;
+
+    @SpringBean
+    private SecurityQuestionRestClient securityQuestionRestClient;
+
+    @SpringBean
+    private WorkflowRestClient wfRestClient;
+
+    @SpringBean
+    private PreferenceManager prefMan;
+
+    @SpringBean
+    private ImplementationClassNamesLoader implementationClassNamesLoader;
+
+    private final ModalWindow syncopeConfWin;
+
+    private final ModalWindow createNotificationWin;
+
+    private final ModalWindow editNotificationWin;
+
+    private final ModalWindow createSecurityQuestionWin;
+
+    private final ModalWindow editSecurityQuestionWin;
+
+    private WebMarkupContainer notificationContainer;
+
+    private WebMarkupContainer securityQuestionContainer;
+
+    private int notificationPaginatorRows;
+
+    public Configuration() {
+        super();
+
+        // Layouts
+        add(new LayoutsPanel("adminUserLayoutPanel", AttrLayoutType.ADMIN_USER, feedbackPanel));
+        add(new LayoutsPanel("selfUserLayoutPanel", AttrLayoutType.SELF_USER, feedbackPanel));
+        add(new LayoutsPanel("adminRoleLayoutPanel", AttrLayoutType.ADMIN_ROLE, feedbackPanel));
+        add(new LayoutsPanel("selfRoleLayoutPanel", AttrLayoutType.SELF_ROLE, feedbackPanel));
+        add(new LayoutsPanel("adminMembershipLayoutPanel", AttrLayoutType.ADMIN_MEMBERSHIP, feedbackPanel));
+        add(new LayoutsPanel("selfMembershipLayoutPanel", AttrLayoutType.SELF_MEMBERSHIP, feedbackPanel));
+
+        add(syncopeConfWin = new ModalWindow("syncopeConfWin"));
+        syncopeConfWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        syncopeConfWin.setInitialHeight(SYNCOPECONF_WIN_HEIGHT);
+        syncopeConfWin.setInitialWidth(SYNCOPECONF_WIN_WIDTH);
+        syncopeConfWin.setCookieName("syncopeconf-modal");
+        setupSyncopeConf();
+
+        add(new PoliciesPanel("passwordPoliciesPanel", getPageReference(), PolicyType.PASSWORD));
+        add(new PoliciesPanel("accountPoliciesPanel", getPageReference(), PolicyType.ACCOUNT));
+        add(new PoliciesPanel("syncPoliciesPanel", getPageReference(), PolicyType.SYNC));
+
+        add(createNotificationWin = new ModalWindow("createNotificationWin"));
+        createNotificationWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        createNotificationWin.setInitialHeight(NOTIFICATION_WIN_HEIGHT);
+        createNotificationWin.setInitialWidth(NOTIFICATION_WIN_WIDTH);
+        createNotificationWin.setCookieName("create-notification-modal");
+        add(editNotificationWin = new ModalWindow("editNotificationWin"));
+        editNotificationWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        editNotificationWin.setInitialHeight(NOTIFICATION_WIN_HEIGHT);
+        editNotificationWin.setInitialWidth(NOTIFICATION_WIN_WIDTH);
+        editNotificationWin.setCookieName("edit-notification-modal");
+        setupNotification();
+
+        add(createSecurityQuestionWin = new ModalWindow("createSecurityQuestionWin"));
+        createSecurityQuestionWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        createSecurityQuestionWin.setInitialHeight(SECURITY_QUESTION_WIN_HEIGHT);
+        createSecurityQuestionWin.setInitialWidth(SECURITY_QUESTION_WIN_WIDTH);
+        createSecurityQuestionWin.setCookieName("create-security-question-modal");
+        add(editSecurityQuestionWin = new ModalWindow("editSecurityQuestionWin"));
+        editSecurityQuestionWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        editSecurityQuestionWin.setInitialHeight(SECURITY_QUESTION_WIN_HEIGHT);
+        editSecurityQuestionWin.setInitialWidth(SECURITY_QUESTION_WIN_WIDTH);
+        editSecurityQuestionWin.setCookieName("edit-security-question-modal");
+        setupSecurityQuestion();
+
+        // Workflow definition stuff
+        WebMarkupContainer noActivitiEnabledForUsers = new WebMarkupContainer("noActivitiEnabledForUsers");
+        noActivitiEnabledForUsers.setOutputMarkupPlaceholderTag(true);
+        add(noActivitiEnabledForUsers);
+
+        WebMarkupContainer workflowDefContainer = new WebMarkupContainer("workflowDefContainer");
+        workflowDefContainer.setOutputMarkupPlaceholderTag(true);
+
+        if (wfRestClient.isActivitiEnabledForUsers()) {
+            noActivitiEnabledForUsers.setVisible(false);
+        } else {
+            workflowDefContainer.setVisible(false);
+        }
+
+        BookmarkablePageLink<Void> activitiModeler =
+                new BookmarkablePageLink<>("activitiModeler", ActivitiModelerPopupPage.class);
+        activitiModeler.setPopupSettings(new VeilPopupSettings().setHeight(600).setWidth(800));
+        MetaDataRoleAuthorizationStrategy.authorize(activitiModeler, ENABLE,
+                xmlRolesReader.getEntitlement("Configuration", "workflowDefRead"));
+        workflowDefContainer.add(activitiModeler);
+        // Check if Activiti Modeler directory is found
+        boolean activitiModelerEnabled = false;
+        try {
+            String activitiModelerDirectory = WebApplicationContextUtils.getWebApplicationContext(
+                    WebApplication.get().getServletContext()).getBean("activitiModelerDirectory", String.class);
+            File baseDir = new File(activitiModelerDirectory);
+            activitiModelerEnabled = baseDir.exists() && baseDir.canRead() && baseDir.isDirectory();
+        } catch (Exception e) {
+            LOG.error("Could not check for Activiti Modeler directory", e);
+        }
+        activitiModeler.setEnabled(activitiModelerEnabled);
+
+        BookmarkablePageLink<Void> xmlEditor = new BookmarkablePageLink<>("xmlEditor", XMLEditorPopupPage.class);
+        xmlEditor.setPopupSettings(new VeilPopupSettings().setHeight(480).setWidth(800));
+        MetaDataRoleAuthorizationStrategy.authorize(xmlEditor, ENABLE,
+                xmlRolesReader.getEntitlement("Configuration", "workflowDefRead"));
+        workflowDefContainer.add(xmlEditor);
+
+        Image workflowDefDiagram = new Image("workflowDefDiagram", new Model()) {
+
+            private static final long serialVersionUID = -8457850449086490660L;
+
+            @Override
+            protected IResource getImageResource() {
+                return new DynamicImageResource() {
+
+                    private static final long serialVersionUID = 923201517955737928L;
+
+                    @Override
+                    protected byte[] getImageData(final IResource.Attributes attributes) {
+                        return wfRestClient.isActivitiEnabledForUsers()
+                                ? wfRestClient.getDiagram()
+                                : new byte[0];
+                    }
+                };
+            }
+        };
+        workflowDefContainer.add(workflowDefDiagram);
+
+        MetaDataRoleAuthorizationStrategy.authorize(workflowDefContainer, ENABLE,
+                xmlRolesReader.getEntitlement("Configuration", "workflowDefRead"));
+        add(workflowDefContainer);
+
+        // Logger stuff
+        PropertyListView<LoggerTO> coreLoggerList =
+                new LoggerPropertyList(null, "corelogger", loggerRestClient.listLogs());
+        WebMarkupContainer coreLoggerContainer = new WebMarkupContainer("coreLoggerContainer");
+        coreLoggerContainer.add(coreLoggerList);
+        coreLoggerContainer.setOutputMarkupId(true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(coreLoggerContainer, ENABLE, xmlRolesReader.getEntitlement(
+                "Configuration", "logList"));
+        add(coreLoggerContainer);
+
+        ConsoleLoggerController consoleLoggerController = new ConsoleLoggerController();
+        PropertyListView<LoggerTO> consoleLoggerList =
+                new LoggerPropertyList(consoleLoggerController, "consolelogger", consoleLoggerController.getLoggers());
+        WebMarkupContainer consoleLoggerContainer = new WebMarkupContainer("consoleLoggerContainer");
+        consoleLoggerContainer.add(consoleLoggerList);
+        consoleLoggerContainer.setOutputMarkupId(true);
+
+        MetaDataRoleAuthorizationStrategy.authorize(
+                consoleLoggerContainer, ENABLE, xmlRolesReader.getEntitlement("Configuration", "logList"));
+        add(consoleLoggerContainer);
+
+        // Extension panels
+        setupExtPanels();
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private void setupSyncopeConf() {
+        final WebMarkupContainer parameters = new WebMarkupContainer("parameters");
+        parameters.setOutputMarkupId(true);
+        add(parameters);
+
+        setWindowClosedCallback(syncopeConfWin, parameters);
+
+        AjaxLink<Void> confLink = new IndicatingAjaxLink<Void>("confLink") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                syncopeConfWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+                        return new ConfModalPage(getPageReference(), editNotificationWin, parameters);
+                    }
+                });
+
+                syncopeConfWin.show(target);
+            }
+        };
+        parameters.add(confLink);
+
+        Link<Void> dbExportLink = new Link<Void>("dbExportLink") {
+
+            private static final long serialVersionUID = -4331619903296515985L;
+
+            @Override
+            public void onClick() {
+                try {
+                    HttpResourceStream stream = new HttpResourceStream(confRestClient.dbExport());
+
+                    ResourceStreamRequestHandler rsrh = new ResourceStreamRequestHandler(stream);
+                    rsrh.setFileName(stream.getFilename() == null ? "content.xml" : stream.getFilename());
+                    rsrh.setContentDisposition(ContentDisposition.ATTACHMENT);
+
+                    getRequestCycle().scheduleRequestHandlerAfterCurrent(rsrh);
+                } catch (Exception e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                }
+            }
+        };
+        MetaDataRoleAuthorizationStrategy.authorize(
+                dbExportLink, ENABLE, xmlRolesReader.getEntitlement("Configuration", "export"));
+        add(dbExportLink);
+    }
+
+    private void setupNotification() {
+        notificationPaginatorRows = prefMan.getPaginatorRows(getRequest(), Constants.PREF_NOTIFICATION_PAGINATOR_ROWS);
+
+        final List<IColumn<NotificationTO, String>> notificationCols = new ArrayList<>();
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("key"), "key", "key"));
+        notificationCols.add(new CollectionPropertyColumn<NotificationTO>(
+                new ResourceModel("events"), "events", "events"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("subject"), "subject", "subject"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("template"), "template", "template"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("traceLevel"), "traceLevel", "traceLevel"));
+        notificationCols.add(new PropertyColumn<NotificationTO, String>(
+                new ResourceModel("active"), "active", "active"));
+
+        notificationCols.add(new AbstractColumn<NotificationTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public String getCssClass() {
+                return "action";
+            }
+
+            @Override
+            public void populateItem(final Item<ICellPopulator<NotificationTO>> cellItem, final String componentId,
+                    final IModel<NotificationTO> model) {
+
+                final NotificationTO notificationTO = model.getObject();
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        editNotificationWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new NotificationModalPage(Configuration.this.getPageReference(),
+                                        editNotificationWin, notificationTO, false);
+                            }
+                        });
+
+                        editNotificationWin.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, "Notification");
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        try {
+                            notificationRestClient.delete(notificationTO.getKey());
+                        } catch (SyncopeClientException e) {
+                            LOG.error("While deleting a notification", e);
+                            error(e.getMessage());
+                            return;
+                        }
+
+                        info(getString(Constants.OPERATION_SUCCEEDED));
+                        feedbackPanel.refresh(target);
+                        target.add(notificationContainer);
+                    }
+                }, ActionLink.ActionType.DELETE, "Notification");
+
+                cellItem.add(panel);
+            }
+        });
+
+        final AjaxFallbackDefaultDataTable<NotificationTO, String> notificationTable =
+                new AjaxFallbackDefaultDataTable<>(
+                        "notificationTable", notificationCols, new NotificationProvider(), notificationPaginatorRows);
+
+        notificationContainer = new WebMarkupContainer("notificationContainer");
+        notificationContainer.add(notificationTable);
+        notificationContainer.setOutputMarkupId(true);
+
+        add(notificationContainer);
+
+        setWindowClosedCallback(createNotificationWin, notificationContainer);
+        setWindowClosedCallback(editNotificationWin, notificationContainer);
+
+        AjaxLink<Void> createNotificationLink = new AjaxLink<Void>("createNotificationLink") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                createNotificationWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+                        return new NotificationModalPage(Configuration.this.getPageReference(), createNotificationWin,
+                                new NotificationTO(), true);
+                    }
+                });
+
+                createNotificationWin.show(target);
+            }
+        };
+
+        MetaDataRoleAuthorizationStrategy.authorize(createNotificationLink, ENABLE, xmlRolesReader.getEntitlement(
+                "Notification", "create"));
+        add(createNotificationLink);
+
+        @SuppressWarnings("rawtypes")
+        Form notificationPaginatorForm = new Form("notificationPaginatorForm");
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final DropDownChoice rowsChooser = new DropDownChoice("rowsChooser", new PropertyModel(this,
+                "notificationPaginatorRows"), prefMan.getPaginatorChoices());
+
+        rowsChooser.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                prefMan.set(getRequest(), getResponse(), Constants.PREF_NOTIFICATION_PAGINATOR_ROWS, String.valueOf(
+                        notificationPaginatorRows));
+                notificationTable.setItemsPerPage(notificationPaginatorRows);
+
+                target.add(notificationContainer);
+            }
+        });
+
+        notificationPaginatorForm.add(rowsChooser);
+        add(notificationPaginatorForm);
+    }
+
+    private void setupSecurityQuestion() {
+        final List<IColumn<SecurityQuestionTO, String>> securityQuestionCols = new ArrayList<>();
+        securityQuestionCols.add(new PropertyColumn<SecurityQuestionTO, String>(
+                new ResourceModel("key"), "key", "key"));
+        securityQuestionCols.add(new PropertyColumn<SecurityQuestionTO, String>(
+                new ResourceModel("content"), "content", "content"));
+
+        securityQuestionCols.add(new AbstractColumn<SecurityQuestionTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = 2054811145491901166L;
+
+            @Override
+            public String getCssClass() {
+                return "action";
+            }
+
+            @Override
+            public void populateItem(final Item<ICellPopulator<SecurityQuestionTO>> cellItem, final String componentId,
+                    final IModel<SecurityQuestionTO> model) {
+
+                final SecurityQuestionTO securityQuestionTO = model.getObject();
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        editSecurityQuestionWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new SecurityQuestionModalPage(Configuration.this.getPageReference(),
+                                        editSecurityQuestionWin, securityQuestionTO, false);
+                            }
+                        });
+
+                        editSecurityQuestionWin.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, "SecurityQuestion");
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -3722207913631435501L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        try {
+                            securityQuestionRestClient.delete(securityQuestionTO.getKey());
+                        } catch (SyncopeClientException e) {
+                            LOG.error("While deleting a security question", e);
+                            error(e.getMessage());
+                            return;
+                        }
+
+                        info(getString(Constants.OPERATION_SUCCEEDED));
+                        feedbackPanel.refresh(target);
+                        target.add(securityQuestionContainer);
+                    }
+                }, ActionLink.ActionType.DELETE, "SecurityQuestion");
+
+                cellItem.add(panel);
+            }
+        });
+
+        final AjaxFallbackDefaultDataTable<SecurityQuestionTO, String> securityQuestionTable =
+                new AjaxFallbackDefaultDataTable<>("securityQuestionTable",
+                        securityQuestionCols, new SecurityQuestionProvider(), 50);
+
+        securityQuestionContainer = new WebMarkupContainer("securityQuestionContainer");
+        securityQuestionContainer.add(securityQuestionTable);
+        securityQuestionContainer.setOutputMarkupId(true);
+
+        add(securityQuestionContainer);
+
+        setWindowClosedCallback(createSecurityQuestionWin, securityQuestionContainer);
+        setWindowClosedCallback(editSecurityQuestionWin, securityQuestionContainer);
+
+        AjaxLink<Void> createSecurityQuestionLink = new AjaxLink<Void>("createSecurityQuestionLink") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+
+                createSecurityQuestionWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+                        return new SecurityQuestionModalPage(Configuration.this.getPageReference(),
+                                createSecurityQuestionWin, new SecurityQuestionTO(), true);
+                    }
+                });
+
+                createSecurityQuestionWin.show(target);
+            }
+        };
+
+        MetaDataRoleAuthorizationStrategy.authorize(
+                createSecurityQuestionLink, ENABLE, xmlRolesReader.getEntitlement("SecurityQuestion", "create"));
+        add(createSecurityQuestionLink);
+    }
+
+    private void setupExtPanels() {
+        List<AbstractTab> tabs = new ArrayList<>();
+        int index = 0;
+        for (final Class<? extends AbstractExtensionPanel> clazz
+                : implementationClassNamesLoader.getExtPanelClasses()) {
+
+            String title = clazz.getAnnotation(ExtensionPanel.class) == null
+                    ? "Extension " + index
+                    : clazz.getAnnotation(ExtensionPanel.class).value();
+            tabs.add(new AbstractTab(new Model<>(title)) {
+
+                private static final long serialVersionUID = -5861786415855103549L;
+
+                @Override
+                public WebMarkupContainer getPanel(final String panelId) {
+                    Panel panel;
+
+                    try {
+                        panel = ClassUtils.getConstructorIfAvailable(clazz, String.class, PageReference.class).
+                                newInstance(panelId, Configuration.this.getPageReference());
+                    } catch (Exception e) {
+                        panel = new Panel(panelId) {
+
+                            private static final long serialVersionUID = 5538299138211283825L;
+
+                        };
+
+                        LOG.error("Could not instantiate {}", clazz.getName(), e);
+                    }
+
+                    return panel;
+                }
+            });
+
+            index++;
+        }
+
+        JQueryUITabbedPanel<AbstractTab> extPanels = new JQueryUITabbedPanel<>("extPanels", tabs);
+        extPanels.setVisible(!tabs.isEmpty());
+        add(extPanels);
+    }
+
+    private class NotificationProvider extends SortableDataProvider<NotificationTO, String> {
+
+        private static final long serialVersionUID = -276043813563988590L;
+
+        private final SortableDataProviderComparator<NotificationTO> comparator;
+
+        public NotificationProvider() {
+            //Default sorting
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<NotificationTO> iterator(final long first, final long count) {
+            List<NotificationTO> list = notificationRestClient.getAllNotifications();
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return notificationRestClient.getAllNotifications().size();
+        }
+
+        @Override
+        public IModel<NotificationTO> model(final NotificationTO notification) {
+            return new AbstractReadOnlyModel<NotificationTO>() {
+
+                private static final long serialVersionUID = 774694801558497248L;
+
+                @Override
+                public NotificationTO getObject() {
+                    return notification;
+                }
+            };
+        }
+    }
+
+    private class SecurityQuestionProvider extends SortableDataProvider<SecurityQuestionTO, String> {
+
+        private static final long serialVersionUID = -1458398823626281188L;
+
+        private final SortableDataProviderComparator<SecurityQuestionTO> comparator;
+
+        public SecurityQuestionProvider() {
+            //Default sorting
+            setSort("key", SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<SecurityQuestionTO> iterator(final long first, final long count) {
+            List<SecurityQuestionTO> list = securityQuestionRestClient.list();
+
+            Collections.sort(list, comparator);
+
+            return list.subList((int) first, (int) first + (int) count).iterator();
+        }
+
+        @Override
+        public long size() {
+            return securityQuestionRestClient.list().size();
+        }
+
+        @Override
+        public IModel<SecurityQuestionTO> model(final SecurityQuestionTO securityQuestionTO) {
+            return new AbstractReadOnlyModel<SecurityQuestionTO>() {
+
+                private static final long serialVersionUID = 5079291243768775704L;
+
+                @Override
+                public SecurityQuestionTO getObject() {
+                    return securityQuestionTO;
+                }
+            };
+        }
+    }
+
+    private class LoggerPropertyList extends PropertyListView<LoggerTO> {
+
+        private static final long serialVersionUID = 5911412425994616111L;
+
+        private final ConsoleLoggerController consoleLoggerController;
+
+        public LoggerPropertyList(final ConsoleLoggerController consoleLoggerController, final String id,
+                final List<? extends LoggerTO> list) {
+
+            super(id, list);
+            this.consoleLoggerController = consoleLoggerController;
+        }
+
+        @Override
+        protected void populateItem(final ListItem<LoggerTO> item) {
+            item.add(new Label("key"));
+
+            DropDownChoice<LoggerLevel> level = new DropDownChoice<>("level");
+            level.setModel(new IModel<LoggerLevel>() {
+
+                private static final long serialVersionUID = -2350428186089596562L;
+
+                @Override
+                public LoggerLevel getObject() {
+                    return item.getModelObject().getLevel();
+                }
+
+                @Override
+                public void setObject(final LoggerLevel object) {
+                    item.getModelObject().setLevel(object);
+                }
+
+                @Override
+                public void detach() {
+                }
+            });
+            level.setChoices(Arrays.asList(LoggerLevel.values()));
+            level.setOutputMarkupId(true);
+            level.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    try {
+                        if (getId().equals("corelogger")) {
+                            loggerRestClient.setLogLevel(item.getModelObject().getKey(),
+                                    item.getModelObject().getLevel());
+                        } else {
+                            consoleLoggerController.setLogLevel(item.getModelObject().getKey(),
+                                    item.getModelObject().getLevel());
+                        }
+
+                        info(getString(Constants.OPERATION_SUCCEEDED));
+                    } catch (SyncopeClientException e) {
+                        info(getString(Constants.OPERATION_ERROR));
+                    }
+
+                    feedbackPanel.refresh(target);
+                }
+            });
+
+            MetaDataRoleAuthorizationStrategy.authorize(level, ENABLE, xmlRolesReader.getEntitlement(
+                    "Configuration", "logSetLevel"));
+
+            item.add(level);
+        }
+    }
+
+    private static class ConsoleLoggerController implements Serializable {
+
+        private static final long serialVersionUID = -1550459341476431714L;
+
+        public List<LoggerTO> getLoggers() {
+            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+
+            List<LoggerTO> result = new ArrayList<>();
+            for (LoggerConfig logger : ctx.getConfiguration().getLoggers().values()) {
+                final String loggerName = LogManager.ROOT_LOGGER_NAME.equals(logger.getName())
+                        ? SyncopeConstants.ROOT_LOGGER : logger.getName();
+                if (logger.getLevel() != null) {
+                    LoggerTO loggerTO = new LoggerTO();
+                    loggerTO.setKey(loggerName);
+                    loggerTO.setLevel(LoggerLevel.fromLevel(logger.getLevel()));
+                    result.add(loggerTO);
+                }
+            }
+
+            return result;
+        }
+
+        public void setLogLevel(final String name, final LoggerLevel level) {
+            LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+            LoggerConfig logConf = SyncopeConstants.ROOT_LOGGER.equals(name)
+                    ? ctx.getConfiguration().getLoggerConfig(LogManager.ROOT_LOGGER_NAME)
+                    : ctx.getConfiguration().getLoggerConfig(name);
+            logConf.setLevel(level.getLevel());
+            ctx.updateLoggers();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfirmPasswordResetModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfirmPasswordResetModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfirmPasswordResetModalPage.java
new file mode 100644
index 0000000..ebc041d
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConfirmPasswordResetModalPage.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.Mode;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.StatelessForm;
+import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+
+public class ConfirmPasswordResetModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -8419445804421211904L;
+
+    public ConfirmPasswordResetModalPage(final ModalWindow window, final String token) {
+        super();
+        setOutputMarkupId(true);
+
+        final StatelessForm<?> form = new StatelessForm<Object>(FORM);
+        form.setOutputMarkupId(true);
+
+        final FieldPanel<String> password =
+                new AjaxPasswordFieldPanel("password", "password", new Model<String>()).setRequired(true);
+        ((PasswordTextField) password.getField()).setResetPassword(true);
+        form.add(password);
+
+        final FieldPanel<String> confirmPassword =
+                new AjaxPasswordFieldPanel("confirmPassword", "confirmPassword", new Model<String>());
+        ((PasswordTextField) confirmPassword.getField()).setResetPassword(true);
+        form.add(confirmPassword);
+
+        form.add(new EqualPasswordInputValidator(password.getField(), confirmPassword.getField()));
+
+        final AjaxButton submit = new IndicatingAjaxButton(APPLY, new ResourceModel(SUBMIT, SUBMIT)) {
+
+            private static final long serialVersionUID = -4804368561204623354L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                try {
+                    userSelfRestClient.confirmPasswordReset(token, password.getModelObject());
+
+                    setResponsePage(new ResultStatusModalPage.Builder(window, new UserTO()).mode(Mode.SELF).build());
+                } catch (Exception e) {
+                    LOG.error("While confirming password reset for {}", token, e);
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                    feedbackPanel.refresh(target);
+                }
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                feedbackPanel.refresh(target);
+            }
+        };
+        form.add(submit);
+        form.setDefaultButton(submit);
+
+        final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+            }
+        };
+        cancel.setDefaultFormProcessing(false);
+        form.add(cancel);
+
+        add(form);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnObjectModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnObjectModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnObjectModalPage.java
new file mode 100644
index 0000000..cf1c405
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnObjectModalPage.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.list.AltListView;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.util.ListModel;
+
+public class ConnObjectModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -6469290753080058487L;
+
+    public ConnObjectModalPage(final ConnObjectTO connObjectTO) {
+        super();
+
+        final Form<Void> form = new Form<Void>(FORM);
+        form.setEnabled(false);
+        add(form);
+
+        IModel<List<AttrTO>> formProps = new LoadableDetachableModel<List<AttrTO>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<AttrTO> load() {
+                List<AttrTO> attrs = connObjectTO.getPlainAttrs();
+                Collections.sort(attrs, new Comparator<AttrTO>() {
+
+                    @Override
+                    public int compare(final AttrTO attr1, final AttrTO attr2) {
+                        if (attr1 == null || attr1.getSchema() == null) {
+                            return -1;
+                        }
+                        if (attr2 == null || attr2.getSchema() == null) {
+                            return 1;
+                        }
+                        return attr1.getSchema().compareTo(attr2.getSchema());
+                    }
+                });
+
+                return attrs;
+            }
+        };
+        final ListView<AttrTO> propView = new AltListView<AttrTO>("propView", formProps) {
+
+            private static final long serialVersionUID = 3109256773218160485L;
+
+            @Override
+            protected void populateItem(final ListItem<AttrTO> item) {
+                final AttrTO prop = item.getModelObject();
+
+                Label label = new Label("key", prop.getSchema());
+                item.add(label);
+
+                Panel field;
+                if (prop.getValues().isEmpty()) {
+                    field = new AjaxTextFieldPanel("value",
+                            prop.getSchema(), new Model<String>());
+                } else if (prop.getValues().size() == 1) {
+                    field = new AjaxTextFieldPanel("value",
+                            prop.getSchema(), new Model<String>(prop.getValues().get(0)));
+                } else {
+                    field = new MultiFieldPanel<String>("value", new ListModel<String>(prop.getValues()),
+                            new AjaxTextFieldPanel("panel", prop.getSchema(), new Model<String>()));
+                }
+                item.add(field);
+            }
+        };
+        form.add(propView);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnectorModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnectorModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnectorModalPage.java
new file mode 100644
index 0000000..8095591
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/ConnectorModalPage.java
@@ -0,0 +1,479 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.list.ConnConfPropertyListView;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.ConnBundleTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.common.lib.types.ConnConfPropSchema;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.ConnectorCapability;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.apache.wicket.validation.validator.RangeValidator;
+
+/**
+ * Modal window with Connector form.
+ */
+public class ConnectorModalPage extends BaseModalPage {
+
+    private static final long serialVersionUID = -2025535531121434050L;
+
+    @SpringBean
+    private ConnectorRestClient restClient;
+
+    private final Map<String, Map<String, Map<String, ConnBundleTO>>> mapConnBundleTOs;
+
+    private final List<ConnectorCapability> selectedCapabilities;
+
+    private ConnBundleTO bundleTO;
+
+    private List<ConnConfProperty> properties;
+
+    private final WebMarkupContainer propertiesContainer;
+
+    public ConnectorModalPage(final PageReference pageRef, final ModalWindow window,
+            final ConnInstanceTO connInstanceTO) {
+
+        super();
+
+        this.add(new Label("new", connInstanceTO.getKey() == 0
+                ? new ResourceModel("new")
+                : new Model<>(StringUtils.EMPTY)));
+        this.add(new Label("key", connInstanceTO.getKey() == 0
+                ? StringUtils.EMPTY
+                : connInstanceTO.getKey()));
+
+        // general data setup
+        selectedCapabilities = new ArrayList<>(connInstanceTO.getKey() == 0
+                ? EnumSet.noneOf(ConnectorCapability.class)
+                : connInstanceTO.getCapabilities());
+
+        mapConnBundleTOs = new HashMap<>();
+        for (ConnBundleTO connBundleTO : restClient.getAllBundles()) {
+            // by location
+            if (!mapConnBundleTOs.containsKey(connBundleTO.getLocation())) {
+                mapConnBundleTOs.put(connBundleTO.getLocation(), new HashMap<String, Map<String, ConnBundleTO>>());
+            }
+            final Map<String, Map<String, ConnBundleTO>> byLocation = mapConnBundleTOs.get(connBundleTO.getLocation());
+
+            // by name
+            if (!byLocation.containsKey(connBundleTO.getBundleName())) {
+                byLocation.put(connBundleTO.getBundleName(), new HashMap<String, ConnBundleTO>());
+            }
+            final Map<String, ConnBundleTO> byName = byLocation.get(connBundleTO.getBundleName());
+
+            // by version
+            if (!byName.containsKey(connBundleTO.getVersion())) {
+                byName.put(connBundleTO.getVersion(), connBundleTO);
+            }
+        }
+
+        bundleTO = getSelectedBundleTO(connInstanceTO);
+        properties = fillProperties(bundleTO, connInstanceTO);
+
+        // form - first tab
+        final Form<ConnInstanceTO> connectorForm = new Form<>(FORM);
+        connectorForm.setModel(new CompoundPropertyModel<>(connInstanceTO));
+        connectorForm.setOutputMarkupId(true);
+        add(connectorForm);
+
+        propertiesContainer = new WebMarkupContainer("container");
+        propertiesContainer.setOutputMarkupId(true);
+        connectorForm.add(propertiesContainer);
+
+        final Form<ConnInstanceTO> connectorPropForm = new Form<>("connectorPropForm");
+        connectorPropForm.setModel(new CompoundPropertyModel<>(connInstanceTO));
+        connectorPropForm.setOutputMarkupId(true);
+        propertiesContainer.add(connectorPropForm);
+
+        final AjaxTextFieldPanel displayName = new AjaxTextFieldPanel(
+                "displayName", "display name", new PropertyModel<String>(connInstanceTO, "displayName"));
+        displayName.setOutputMarkupId(true);
+        displayName.addRequiredLabel();
+        connectorForm.add(displayName);
+
+        final AjaxDropDownChoicePanel<String> location =
+                new AjaxDropDownChoicePanel<>("location", "location",
+                        new Model<>(bundleTO == null ? null : bundleTO.getLocation()));
+        ((DropDownChoice<String>) location.getField()).setNullValid(true);
+        location.setStyleSheet("long_dynamicsize");
+        location.setChoices(new ArrayList<>(mapConnBundleTOs.keySet()));
+        location.setRequired(true);
+        location.addRequiredLabel();
+        location.setOutputMarkupId(true);
+        location.setEnabled(connInstanceTO.getKey() == 0);
+        location.getField().setOutputMarkupId(true);
+        connectorForm.add(location);
+
+        final AjaxDropDownChoicePanel<String> connectorName =
+                new AjaxDropDownChoicePanel<>("connectorName", "connectorName",
+                        new Model<>(bundleTO == null ? null : bundleTO.getBundleName()));
+        ((DropDownChoice<String>) connectorName.getField()).setNullValid(true);
+        connectorName.setStyleSheet("long_dynamicsize");
+        connectorName.setChoices(bundleTO == null
+                ? new ArrayList<String>()
+                : new ArrayList<>(mapConnBundleTOs.get(connInstanceTO.getLocation()).keySet()));
+        connectorName.setRequired(true);
+        connectorName.addRequiredLabel();
+        connectorName.setEnabled(connInstanceTO.getLocation() != null);
+        connectorName.setOutputMarkupId(true);
+        connectorName.setEnabled(connInstanceTO.getKey() == 0);
+        connectorName.getField().setOutputMarkupId(true);
+        connectorForm.add(connectorName);
+
+        final AjaxDropDownChoicePanel<String> version =
+                new AjaxDropDownChoicePanel<>("version", "version",
+                        new Model<>(bundleTO == null ? null : bundleTO.getVersion()));
+        version.setStyleSheet("long_dynamicsize");
+        version.setChoices(bundleTO == null
+                ? new ArrayList<String>()
+                : new ArrayList<>(mapConnBundleTOs.get(connInstanceTO.getLocation()).
+                        get(connInstanceTO.getBundleName()).keySet()));
+        version.setRequired(true);
+        version.addRequiredLabel();
+        version.setEnabled(connInstanceTO.getBundleName() != null);
+        version.setOutputMarkupId(true);
+        version.addRequiredLabel();
+        version.getField().setOutputMarkupId(true);
+        connectorForm.add(version);
+
+        final SpinnerFieldPanel<Integer> connRequestTimeout =
+                new SpinnerFieldPanel<>("connRequestTimeout", "connRequestTimeout", Integer.class,
+                        new PropertyModel<Integer>(connInstanceTO, "connRequestTimeout"), 0, null);
+        connRequestTimeout.getField().add(new RangeValidator<>(0, Integer.MAX_VALUE));
+        connectorForm.add(connRequestTimeout);
+
+        if (connInstanceTO.getPoolConf() == null) {
+            connInstanceTO.setPoolConf(new ConnPoolConfTO());
+        }
+        final SpinnerFieldPanel<Integer> poolMaxObjects =
+                new SpinnerFieldPanel<>("poolMaxObjects", "poolMaxObjects", Integer.class,
+                        new PropertyModel<Integer>(connInstanceTO.getPoolConf(), "maxObjects"), 0, null);
+        poolMaxObjects.getField().add(new RangeValidator<>(0, Integer.MAX_VALUE));
+        connectorForm.add(poolMaxObjects);
+        final SpinnerFieldPanel<Integer> poolMinIdle =
+                new SpinnerFieldPanel<>("poolMinIdle", "poolMinIdle", Integer.class,
+                        new PropertyModel<Integer>(connInstanceTO.getPoolConf(), "minIdle"), 0, null);
+        poolMinIdle.getField().add(new RangeValidator<>(0, Integer.MAX_VALUE));
+        connectorForm.add(poolMinIdle);
+        final SpinnerFieldPanel<Integer> poolMaxIdle =
+                new SpinnerFieldPanel<>("poolMaxIdle", "poolMaxIdle", Integer.class,
+                        new PropertyModel<Integer>(connInstanceTO.getPoolConf(), "maxIdle"), 0, null);
+        poolMaxIdle.getField().add(new RangeValidator<>(0, Integer.MAX_VALUE));
+        connectorForm.add(poolMaxIdle);
+        final SpinnerFieldPanel<Long> poolMaxWait =
+                new SpinnerFieldPanel<>("poolMaxWait", "poolMaxWait", Long.class,
+                        new PropertyModel<Long>(connInstanceTO.getPoolConf(), "maxWait"), 0L, null);
+        poolMaxWait.getField().add(new RangeValidator<>(0L, Long.MAX_VALUE));
+        connectorForm.add(poolMaxWait);
+        final SpinnerFieldPanel<Long> poolMinEvictableIdleTime =
+                new SpinnerFieldPanel<>("poolMinEvictableIdleTime", "poolMinEvictableIdleTime", Long.class,
+                        new PropertyModel<Long>(connInstanceTO.getPoolConf(), "minEvictableIdleTimeMillis"),
+                        0L, null);
+        poolMinEvictableIdleTime.getField().add(new RangeValidator<>(0L, Long.MAX_VALUE));
+        connectorForm.add(poolMinEvictableIdleTime);
+
+        // form - first tab - onchange()
+        location.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                ((DropDownChoice<String>) location.getField()).setNullValid(false);
+                connInstanceTO.setLocation(location.getModelObject());
+                target.add(location);
+
+                connectorName.setChoices(new ArrayList<>(
+                        mapConnBundleTOs.get(location.getModelObject()).keySet()));
+                connectorName.setEnabled(true);
+                connectorName.getField().setModelValue(null);
+                target.add(connectorName);
+
+                version.setChoices(new ArrayList<String>());
+                version.getField().setModelValue(null);
+                version.setEnabled(false);
+                target.add(version);
+
+                properties.clear();
+                target.add(propertiesContainer);
+            }
+        });
+        connectorName.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                ((DropDownChoice<String>) connectorName.getField()).setNullValid(false);
+                connInstanceTO.setBundleName(connectorName.getModelObject());
+                target.add(connectorName);
+
+                List<String> versions = new ArrayList<>(
+                        mapConnBundleTOs.get(location.getModelObject()).get(connectorName.getModelObject()).keySet());
+                version.setChoices(versions);
+                version.setEnabled(true);
+                if (versions.size() == 1) {
+                    selectVersion(target, connInstanceTO, version, versions.get(0));
+                    version.getField().setModelObject(versions.get(0));
+                } else {
+                    version.getField().setModelValue(null);
+                    properties.clear();
+                    target.add(propertiesContainer);
+                }
+                target.add(version);
+            }
+        });
+        version.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                selectVersion(target, connInstanceTO, version, version.getModelObject());
+            }
+        });
+
+        // form - second tab (properties)
+        final ListView<ConnConfProperty> connPropView = new ConnConfPropertyListView("connectorProperties",
+                new PropertyModel<List<ConnConfProperty>>(this, "properties"),
+                true, connInstanceTO.getConfiguration());
+        connPropView.setOutputMarkupId(true);
+        connectorPropForm.add(connPropView);
+
+        final AjaxButton check = new IndicatingAjaxButton("check", new ResourceModel("check")) {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                final ConnInstanceTO conn = (ConnInstanceTO) form.getModelObject();
+
+                // ensure that connector bundle information is in sync
+                conn.setBundleName(bundleTO.getBundleName());
+                conn.setVersion(bundleTO.getVersion());
+                conn.setConnectorName(bundleTO.getConnectorName());
+
+                if (restClient.check(conn)) {
+                    info(getString("success_connection"));
+                } else {
+                    error(getString("error_connection"));
+                }
+
+                feedbackPanel.refresh(target);
+            }
+        };
+        connectorPropForm.add(check);
+
+        // form - third tab (capabilities)
+        final IModel<List<ConnectorCapability>> capabilities =
+                new LoadableDetachableModel<List<ConnectorCapability>>() {
+
+                    private static final long serialVersionUID = 5275935387613157437L;
+
+                    @Override
+                    protected List<ConnectorCapability> load() {
+                        return Arrays.asList(ConnectorCapability.values());
+                    }
+                };
+        CheckBoxMultipleChoice<ConnectorCapability> capabilitiesPalette =
+                new CheckBoxMultipleChoice<>("capabilitiesPalette",
+                        new PropertyModel<List<ConnectorCapability>>(this, "selectedCapabilities"), capabilities);
+
+        capabilitiesPalette.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(AjaxRequestTarget target) {
+            }
+        });
+
+        connectorForm.add(capabilitiesPalette);
+
+        // form - submit / cancel buttons
+        final AjaxButton submit = new IndicatingAjaxButton(APPLY, new Model<>(getString(SUBMIT))) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                final ConnInstanceTO conn = (ConnInstanceTO) form.getModelObject();
+
+                conn.setConnectorName(bundleTO.getConnectorName());
+                conn.setBundleName(bundleTO.getBundleName());
+                conn.setVersion(bundleTO.getVersion());
+                conn.getConfiguration().clear();
+                conn.getConfiguration().addAll(connPropView.getModelObject());
+
+                // Set the model object's capabilities to capabilitiesPalette's converted Set
+                conn.getCapabilities().clear();
+                conn.getCapabilities().addAll(selectedCapabilities.isEmpty()
+                        ? EnumSet.noneOf(ConnectorCapability.class)
+                        : EnumSet.copyOf(selectedCapabilities));
+
+                // Reset pool configuration if all fields are null
+                if (conn.getPoolConf() != null
+                        && conn.getPoolConf().getMaxIdle() == null
+                        && conn.getPoolConf().getMaxObjects() == null
+                        && conn.getPoolConf().getMaxWait() == null
+                        && conn.getPoolConf().getMinEvictableIdleTimeMillis() == null
+                        && conn.getPoolConf().getMinIdle() == null) {
+
+                    conn.setPoolConf(null);
+                }
+
+                try {
+                    if (connInstanceTO.getKey() == 0) {
+                        restClient.create(conn);
+                    } else {
+                        restClient.update(conn);
+                    }
+
+                    ((Resources) pageRef.getPage()).setModalResult(true);
+                    window.close(target);
+                } catch (SyncopeClientException e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                    feedbackPanel.refresh(target);
+                    ((Resources) pageRef.getPage()).setModalResult(false);
+                    LOG.error("While creating or updating connector {}", conn, e);
+                }
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                feedbackPanel.refresh(target);
+            }
+        };
+        String roles = connInstanceTO.getKey() == 0
+                ? xmlRolesReader.getEntitlement("Connectors", "create")
+                : xmlRolesReader.getEntitlement("Connectors", "update");
+        MetaDataRoleAuthorizationStrategy.authorize(submit, ENABLE, roles);
+        connectorForm.add(submit);
+
+        final IndicatingAjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+        };
+        cancel.setDefaultFormProcessing(false);
+        connectorForm.add(cancel);
+    }
+
+    private ConnBundleTO getSelectedBundleTO(final ConnInstanceTO connInstanceTO) {
+        ConnBundleTO result = null;
+        if (connInstanceTO != null
+                && StringUtils.isNotBlank(connInstanceTO.getLocation())
+                && StringUtils.isNotBlank(connInstanceTO.getBundleName())
+                && StringUtils.isNotBlank(connInstanceTO.getVersion())
+                && mapConnBundleTOs.containsKey(connInstanceTO.getLocation())) {
+
+            Map<String, Map<String, ConnBundleTO>> byLocation = mapConnBundleTOs.get(connInstanceTO.getLocation());
+            if (byLocation.containsKey(connInstanceTO.getBundleName())) {
+                Map<String, ConnBundleTO> byName = byLocation.get(connInstanceTO.getBundleName());
+                if (byName.containsKey(connInstanceTO.getVersion())) {
+                    result = byName.get(connInstanceTO.getVersion());
+                }
+            }
+        }
+        return result;
+    }
+
+    private List<ConnConfProperty> fillProperties(final ConnBundleTO bundleTO, final ConnInstanceTO connInstanceTO) {
+        final List<ConnConfProperty> props = new ArrayList<>();
+
+        if (bundleTO != null) {
+            for (ConnConfPropSchema key : bundleTO.getProperties()) {
+                final ConnConfProperty property = new ConnConfProperty();
+                property.setSchema(key);
+                if (connInstanceTO.getKey() != 0
+                        && connInstanceTO.getConfigurationMap().containsKey(key.getName())
+                        && connInstanceTO.getConfigurationMap().get(key.getName()).getValues() != null) {
+
+                    property.getValues().addAll(connInstanceTO.getConfigurationMap().get(key.getName()).getValues());
+                    property.setOverridable(connInstanceTO.getConfigurationMap().get(key.getName()).isOverridable());
+                }
+
+                if (property.getValues().isEmpty() && !key.getDefaultValues().isEmpty()) {
+                    property.getValues().addAll(key.getDefaultValues());
+                }
+
+                props.add(property);
+            }
+        }
+
+        // re-order properties (implements Comparable)
+        Collections.sort(props);
+        return props;
+    }
+
+    private void selectVersion(final AjaxRequestTarget target, final ConnInstanceTO connInstanceTO,
+            final AjaxDropDownChoicePanel<String> version, final String versionValue) {
+
+        connInstanceTO.setVersion(versionValue);
+        target.add(version);
+
+        bundleTO = getSelectedBundleTO(connInstanceTO);
+        properties = fillProperties(bundleTO, connInstanceTO);
+        target.add(propertiesContainer);
+    }
+
+    public List<ConnConfProperty> getProperties() {
+        return properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2d194636/client/console/src/main/java/org/apache/syncope/client/console/pages/DerSchemaModalPage.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/pages/DerSchemaModalPage.java b/client/console/src/main/java/org/apache/syncope/client/console/pages/DerSchemaModalPage.java
new file mode 100644
index 0000000..98f4fbb
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/pages/DerSchemaModalPage.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.pages;
+
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.commons.JexlHelpUtil;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.DerSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+
+/**
+ * Modal window with Schema form.
+ */
+public class DerSchemaModalPage extends AbstractSchemaModalPage<DerSchemaTO> {
+
+    private static final long serialVersionUID = 6668789770131753386L;
+
+    public DerSchemaModalPage(final AttributableType kind) {
+        super(kind);
+    }
+
+    @Override
+    public void setSchemaModalPage(final PageReference pageRef, final ModalWindow window,
+            DerSchemaTO schema, final boolean createFlag) {
+
+        if (schema == null) {
+            schema = new DerSchemaTO();
+        }
+
+        final Form<DerSchemaTO> schemaForm = new Form<>(FORM);
+
+        schemaForm.setModel(new CompoundPropertyModel<>(schema));
+
+        final AjaxTextFieldPanel name =
+                new AjaxTextFieldPanel("key", getString("key"), new PropertyModel<String>(schema, "key"));
+        name.addRequiredLabel();
+
+        final AjaxTextFieldPanel expression = new AjaxTextFieldPanel("expression", getString("expression"),
+                new PropertyModel<String>(schema, "expression"));
+        expression.addRequiredLabel();
+
+        final WebMarkupContainer jexlHelp = JexlHelpUtil.getJexlHelpWebContainer("jexlHelp");
+
+        final AjaxLink<Void> questionMarkJexlHelp = JexlHelpUtil.getAjaxLink(jexlHelp, "questionMarkJexlHelp");
+        schemaForm.add(questionMarkJexlHelp);
+        questionMarkJexlHelp.add(jexlHelp);
+
+        name.setEnabled(createFlag);
+
+        final AjaxButton submit = new IndicatingAjaxButton(APPLY, new ResourceModel(SUBMIT)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form form) {
+                DerSchemaTO schemaTO = (DerSchemaTO) form.getDefaultModelObject();
+
+                try {
+                    if (createFlag) {
+                        schemaRestClient.createDerSchema(kind, schemaTO);
+                    } else {
+                        schemaRestClient.updateDerSchema(kind, schemaTO);
+                    }
+
+                    if (pageRef.getPage() instanceof BasePage) {
+                        ((BasePage) pageRef.getPage()).setModalResult(true);
+                    }
+
+                    window.close(target);
+                } catch (SyncopeClientException e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                    feedbackPanel.refresh(target);
+                }
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                feedbackPanel.refresh(target);
+            }
+        };
+
+        final AjaxButton cancel = new IndicatingAjaxButton(CANCEL, new ResourceModel(CANCEL)) {
+
+            private static final long serialVersionUID = -958724007591692537L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                window.close(target);
+            }
+        };
+
+        cancel.setDefaultFormProcessing(
+                false);
+
+        String allowedRoles = createFlag
+                ? xmlRolesReader.getEntitlement("Schema", "create")
+                : xmlRolesReader.getEntitlement("Schema", "update");
+
+        MetaDataRoleAuthorizationStrategy.authorize(submit, ENABLE, allowedRoles);
+
+        schemaForm.add(name);
+
+        schemaForm.add(expression);
+
+        schemaForm.add(submit);
+
+        schemaForm.add(cancel);
+
+        add(schemaForm);
+    }
+}