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/05 17:01:01 UTC

[30/52] syncope git commit: [SYNCOPE-620] Console (JAR) in, now time for console-reference

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
new file mode 100644
index 0000000..e8b3af7
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/StatusPanel.java
@@ -0,0 +1,263 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.commons.status.ConnObjectWrapper;
+import org.apache.syncope.client.console.commons.status.Status;
+import org.apache.syncope.client.console.commons.status.StatusBean;
+import org.apache.syncope.client.console.commons.status.StatusUtils;
+import org.apache.syncope.client.console.pages.ConnObjectModalPage;
+import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.console.rest.UserRestClient;
+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.list.AltListView;
+import org.apache.syncope.common.lib.mod.StatusMod;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.markup.html.IHeaderContributor;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Check;
+import org.apache.wicket.markup.html.form.CheckGroup;
+import org.apache.wicket.markup.html.form.CheckGroupSelector;
+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.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StatusPanel extends Panel implements IHeaderContributor {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(StatusPanel.class);
+
+    private static final long serialVersionUID = -4064294905566247728L;
+
+    public static final String IMG_STATUES = "../statuses/";
+
+    private static final int CONNOBJECT_WIN_HEIGHT = 400;
+
+    private static final int CONNOBJECT_WIN_WIDTH = 600;
+
+    @SpringBean
+    private UserRestClient userRestClient;
+
+    @SpringBean
+    private RoleRestClient roleRestClient;
+
+    private final ModalWindow connObjectWin;
+
+    private final List<ConnObjectWrapper> connObjects;
+
+    private final Map<String, StatusBean> initialStatusBeanMap;
+
+    private final CheckGroup<StatusBean> checkGroup;
+
+    private final ListView<StatusBean> statusBeansListView;
+
+    private final StatusUtils statusUtils;
+
+    public <T extends AbstractAttributableTO> StatusPanel(
+            final String id,
+            final AbstractSubjectTO subject,
+            final List<StatusBean> selectedResources,
+            final PageReference pageref) {
+
+        super(id);
+
+        connObjectWin = new ModalWindow("connObjectWin");
+        connObjectWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        connObjectWin.setInitialHeight(CONNOBJECT_WIN_HEIGHT);
+        connObjectWin.setInitialWidth(CONNOBJECT_WIN_WIDTH);
+        connObjectWin.setCookieName("connobject-modal");
+        add(connObjectWin);
+
+        statusUtils = new StatusUtils(subject instanceof RoleTO ? roleRestClient : userRestClient);
+
+        connObjects = statusUtils.getConnectorObjects(subject);
+
+        final List<StatusBean> statusBeans = new ArrayList<StatusBean>(connObjects.size() + 1);
+        initialStatusBeanMap = new LinkedHashMap<String, StatusBean>(connObjects.size() + 1);
+
+        final StatusBean syncope = new StatusBean(subject, "syncope");
+
+        if (subject instanceof UserTO) {
+            syncope.setAccountLink(((UserTO) subject).getUsername());
+
+            Status syncopeStatus = Status.UNDEFINED;
+            if (((UserTO) subject).getStatus() != null) {
+                try {
+                    syncopeStatus = Status.valueOf(((UserTO) subject).getStatus().toUpperCase());
+                } catch (IllegalArgumentException e) {
+                    LOG.warn("Unexpected status found: {}", ((UserTO) subject).getStatus(), e);
+                }
+            }
+            syncope.setStatus(syncopeStatus);
+        } else if (subject instanceof RoleTO) {
+            syncope.setAccountLink(((RoleTO) subject).getDisplayName());
+            syncope.setStatus(Status.ACTIVE);
+        }
+
+        statusBeans.add(syncope);
+        initialStatusBeanMap.put(syncope.getResourceName(), syncope);
+
+        for (ConnObjectWrapper entry : connObjects) {
+            final StatusBean statusBean = statusUtils.getStatusBean(
+                    entry.getAttributable(),
+                    entry.getResourceName(),
+                    entry.getConnObjectTO(),
+                    subject instanceof RoleTO);
+
+            initialStatusBeanMap.put(entry.getResourceName(), statusBean);
+            statusBeans.add(statusBean);
+        }
+
+        checkGroup = new CheckGroup<>("group", selectedResources);
+        checkGroup.setOutputMarkupId(true);
+        checkGroup.add(new AjaxFormChoiceComponentUpdatingBehavior() {
+
+            private static final long serialVersionUID = -151291731388673682L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                // ignore
+            }
+        });
+        add(checkGroup);
+
+        CheckGroupSelector groupSelector = new CheckGroupSelector("groupselector", checkGroup);
+        if (subject instanceof RoleTO) {
+            groupSelector.setVisible(false);
+        }
+        add(groupSelector);
+
+        statusBeansListView = new AltListView<StatusBean>("resources", statusBeans) {
+
+            private static final long serialVersionUID = 4949588177564901031L;
+
+            @Override
+            protected void populateItem(final ListItem<StatusBean> item) {
+                item.add(statusUtils.getStatusImage("icon", item.getModelObject().getStatus()));
+
+                final Check<StatusBean> check = new Check<StatusBean>("check", item.getModel(), checkGroup);
+                if (subject instanceof RoleTO) {
+                    check.setVisible(false);
+                }
+                item.add(check);
+
+                item.add(new Label("resource", new ResourceModel(item.getModelObject().getResourceName(), item
+                        .getModelObject().getResourceName())));
+
+                if (StringUtils.isNotBlank(item.getModelObject().getAccountLink())) {
+                    item.add(new Label("accountLink", new ResourceModel(item.getModelObject().getAccountLink(),
+                            item.getModelObject().getAccountLink())));
+                } else {
+                    item.add(new Label("accountLink", ""));
+                }
+
+                final ConnObjectTO connObjectTO = statusUtils.getConnObjectTO(
+                        item.getModelObject().getAttributableId(),
+                        item.getModelObject().getResourceName(),
+                        connObjects);
+
+                if (pageref == null || connObjectTO == null) {
+                    item.add(new Label("connObject", new Model<String>()));
+                } else {
+                    final ActionLinksPanel connObject = new ActionLinksPanel("connObject", new Model(), pageref);
+
+                    connObject.add(new ActionLink() {
+
+                        private static final long serialVersionUID = -3722207913631435501L;
+
+                        @Override
+                        public void onClick(final AjaxRequestTarget target) {
+                            connObjectWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                                private static final long serialVersionUID = -7834632442532690940L;
+
+                                @Override
+                                public Page createPage() {
+                                    return new ConnObjectModalPage(connObjectTO);
+                                }
+                            });
+
+                            connObjectWin.show(target);
+                        }
+                    }, ActionLink.ActionType.SEARCH, "Resources", "getConnectorObject");
+
+                    item.add(connObject);
+                }
+            }
+        };
+        statusBeansListView.setReuseItems(true);
+        checkGroup.add(statusBeansListView);
+    }
+
+    public StatusMod getStatusMod() {
+        StatusMod result = new StatusMod();
+
+        Collection<StatusBean> statusBeans = checkGroup.getModel().getObject();
+        if (statusBeans != null && !statusBeans.isEmpty()) {
+            result = StatusUtils.buildStatusMod(statusBeans);
+        }
+
+        return result;
+    }
+
+    public List<StatusBean> getStatusBeans() {
+        return statusBeansListView.getModelObject();
+    }
+
+    public Map<String, StatusBean> getInitialStatusBeanMap() {
+        return initialStatusBeanMap;
+    }
+
+    public void updateStatusBeans(final List<StatusBean> statusBeans) {
+        statusBeansListView.removeAll();
+        statusBeansListView.getModelObject().clear();
+        statusBeansListView.getModelObject().addAll(statusBeans);
+
+        for (StatusBean statusBean : statusBeans) {
+            if (!checkGroup.getModelObject().contains(statusBean)
+                    && statusBean.getStatus() == Status.NOT_YET_SUBMITTED) {
+
+                checkGroup.getModelObject().add(statusBean);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/SyncTasksPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/SyncTasksPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/SyncTasksPanel.java
new file mode 100644
index 0000000..cf75285
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/SyncTasksPanel.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.RoleTemplateModalPage;
+import org.apache.syncope.client.console.pages.SyncTaskModalPage;
+import org.apache.syncope.client.console.pages.UserTemplateModalPage;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractTaskTO;
+import org.apache.syncope.common.lib.to.SyncTaskTO;
+import org.apache.wicket.Component;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+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.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+public class SyncTasksPanel extends AbstractProvisioningTasksPanel<SyncTaskTO> {
+
+    private static final long serialVersionUID = 53189199346016099L;
+
+    public SyncTasksPanel(final String id, final PageReference pageRef) {
+        super(id, pageRef, SyncTaskTO.class);
+        initTasksTable();
+    }
+
+    @Override
+    protected List<IColumn<AbstractTaskTO, String>> getColumns() {
+        final List<IColumn<AbstractTaskTO, String>> syncTaskscolumns = new ArrayList<IColumn<AbstractTaskTO, String>>();
+
+        syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
+                new StringResourceModel("id", this, null), "id", "id"));
+        syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
+                new StringResourceModel("name", this, null), "name", "name"));
+        syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
+                new StringResourceModel("description", this, null), "description", "description"));
+        syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
+                new StringResourceModel("resourceName", this, null), "resource", "resource"));
+        syncTaskscolumns.add(new DatePropertyColumn<AbstractTaskTO>(
+                new StringResourceModel("lastExec", this, null), "lastExec", "lastExec"));
+        syncTaskscolumns.add(new DatePropertyColumn<AbstractTaskTO>(
+                new StringResourceModel("nextExec", this, null), "nextExec", "nextExec"));
+        syncTaskscolumns.add(new PropertyColumn<AbstractTaskTO, String>(
+                new StringResourceModel("latestExecStatus", this, null), "latestExecStatus", "latestExecStatus"));
+
+        syncTaskscolumns.add(
+                new ActionColumn<AbstractTaskTO, String>(new StringResourceModel("actions", this, null, "")) {
+
+                    private static final long serialVersionUID = 2054811145491901166L;
+
+                    @Override
+                    public ActionLinksPanel getActions(final String componentId, final IModel<AbstractTaskTO> model) {
+
+                        final SyncTaskTO taskTO = (SyncTaskTO) model.getObject();
+
+                        final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, pageRef);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+
+                                window.setPageCreator(new ModalWindow.PageCreator() {
+
+                                    private static final long serialVersionUID = -7834632442532690940L;
+
+                                    @Override
+                                    public Page createPage() {
+                                        return new SyncTaskModalPage(window, taskTO, pageRef);
+                                    }
+                                });
+
+                                window.show(target);
+                            }
+                        }, ActionLink.ActionType.EDIT, TASKS);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+
+                                window.setPageCreator(new ModalWindow.PageCreator() {
+
+                                    private static final long serialVersionUID = -7834632442532690940L;
+
+                                    @Override
+                                    public Page createPage() {
+                                        return new UserTemplateModalPage(pageRef, window, taskTO);
+                                    }
+                                });
+
+                                window.show(target);
+                            }
+                        }, ActionLink.ActionType.USER_TEMPLATE, TASKS);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+
+                                window.setPageCreator(new ModalWindow.PageCreator() {
+
+                                    private static final long serialVersionUID = -7834632442532690940L;
+
+                                    @Override
+                                    public Page createPage() {
+                                        return new RoleTemplateModalPage(pageRef, window, taskTO);
+                                    }
+                                });
+
+                                window.show(target);
+                            }
+                        }, ActionLink.ActionType.ROLE_TEMPLATE, TASKS);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                try {
+                                    restClient.startExecution(taskTO.getKey(), false);
+                                    getSession().info(getString(Constants.OPERATION_SUCCEEDED));
+                                } catch (SyncopeClientException scce) {
+                                    error(scce.getMessage());
+                                }
+
+                                target.add(container);
+                                ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target);
+                            }
+                        }, ActionLink.ActionType.EXECUTE, TASKS);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                try {
+                                    restClient.startExecution(taskTO.getKey(), true);
+                                    getSession().info(getString(Constants.OPERATION_SUCCEEDED));
+                                } catch (SyncopeClientException scce) {
+                                    error(scce.getMessage());
+                                }
+
+                                target.add(container);
+                                ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target);
+                            }
+                        }, ActionLink.ActionType.DRYRUN, TASKS);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -3722207913631435501L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                try {
+                                    restClient.delete(taskTO.getKey(), SyncTaskTO.class);
+                                    info(getString(Constants.OPERATION_SUCCEEDED));
+                                } catch (SyncopeClientException scce) {
+                                    error(scce.getMessage());
+                                }
+                                target.add(container);
+                                ((NotificationPanel) getPage().get(Constants.FEEDBACK)).refresh(target);
+                            }
+                        }, ActionLink.ActionType.DELETE, TASKS);
+
+                        return panel;
+                    }
+
+                    @Override
+                    public Component getHeader(String componentId) {
+                        final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), pageRef);
+
+                        panel.add(new ActionLink() {
+
+                            private static final long serialVersionUID = -7978723352517770644L;
+
+                            @Override
+                            public void onClick(final AjaxRequestTarget target) {
+                                if (target != null) {
+                                    target.add(table);
+                                }
+                            }
+                        }, ActionLink.ActionType.RELOAD, TASKS, "list");
+
+                        return panel;
+                    }
+                });
+
+        return syncTaskscolumns;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDetailsPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDetailsPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDetailsPanel.java
new file mode 100644
index 0000000..18e2066
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserDetailsPanel.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import org.apache.syncope.client.console.commons.JexlHelpUtil;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPasswordFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.behavior.Behavior;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.markup.html.form.validation.EqualPasswordInputValidator;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+
+public class UserDetailsPanel extends Panel {
+
+    private static final long serialVersionUID = 6592027822510220463L;
+
+    public UserDetailsPanel(final String id, final UserTO userTO, final Form form, final boolean resetPassword,
+            final boolean templateMode) {
+
+        super(id);
+
+        // ------------------------
+        // Username
+        // ------------------------
+        final FieldPanel<String> username = new AjaxTextFieldPanel("username", "username",
+                new PropertyModel<String>(userTO, "username"));
+
+        final WebMarkupContainer jexlHelp = JexlHelpUtil.getJexlHelpWebContainer("usernameJexlHelp");
+
+        final AjaxLink<?> questionMarkJexlHelp = JexlHelpUtil.getAjaxLink(jexlHelp, "usernameQuestionMarkJexlHelp");
+        add(questionMarkJexlHelp);
+        questionMarkJexlHelp.add(jexlHelp);
+
+        if (!templateMode) {
+            username.addRequiredLabel();
+            questionMarkJexlHelp.setVisible(false);
+        }
+        add(username);
+        // ------------------------
+
+        // ------------------------
+        // Password
+        // ------------------------
+        final WebMarkupContainer pwdJexlHelp = JexlHelpUtil.getJexlHelpWebContainer("pwdJexlHelp");
+
+        final AjaxLink<?> pwdQuestionMarkJexlHelp = JexlHelpUtil.getAjaxLink(pwdJexlHelp, "pwdQuestionMarkJexlHelp");
+        add(pwdQuestionMarkJexlHelp);
+        pwdQuestionMarkJexlHelp.add(pwdJexlHelp);
+
+        FieldPanel<String> password;
+        Label confirmPasswordLabel = new Label("confirmPasswordLabel", new ResourceModel("confirmPassword"));
+        FieldPanel<String> confirmPassword;
+        if (templateMode) {
+            password = new AjaxTextFieldPanel("password", "password", new PropertyModel<String>(userTO, "password"));
+
+            confirmPasswordLabel.setVisible(false);
+            confirmPassword = new AjaxTextFieldPanel("confirmPassword", "confirmPassword", new Model<String>());
+            confirmPassword.setEnabled(false);
+            confirmPassword.setVisible(false);
+        } else {
+            pwdQuestionMarkJexlHelp.setVisible(false);
+
+            password = new AjaxPasswordFieldPanel("password", "password",
+                    new PropertyModel<String>(userTO, "password"));
+            ((PasswordTextField) password.getField()).setResetPassword(resetPassword);
+
+            confirmPassword = new AjaxPasswordFieldPanel("confirmPassword", "confirmPassword", new Model<String>());
+            if (!resetPassword) {
+                confirmPassword.getField().setModelObject(userTO.getPassword());
+            }
+            ((PasswordTextField) confirmPassword.getField()).setResetPassword(resetPassword);
+
+            form.add(new EqualPasswordInputValidator(password.getField(), confirmPassword.getField()));
+        }
+        add(password);
+        add(confirmPasswordLabel);
+        add(confirmPassword);
+
+        final WebMarkupContainer mandatoryPassword = new WebMarkupContainer("mandatory_pwd");
+        mandatoryPassword.add(new Behavior() {
+
+            private static final long serialVersionUID = 1469628524240283489L;
+
+            @Override
+            public void onComponentTag(final Component component, final ComponentTag tag) {
+                if (userTO.getKey() > 0) {
+                    tag.put("style", "display:none;");
+                }
+            }
+        });
+
+        add(mandatoryPassword);
+        // ------------------------
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchPanel.java
new file mode 100644
index 0000000..2ca1b94
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchPanel.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.search.SyncopeFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class UserSearchPanel extends AbstractSearchPanel {
+
+    private static final long serialVersionUID = -1769527800450203738L;
+
+    @SpringBean
+    private RoleRestClient roleRestClient;
+
+    public static class Builder implements Serializable {
+
+        private static final long serialVersionUID = 6308997285778809578L;
+
+        private String id;
+
+        private String fiql = null;
+
+        private boolean required = true;
+
+        public Builder(final String id) {
+            this.id = id;
+        }
+
+        public Builder fiql(final String fiql) {
+            this.fiql = fiql;
+            return this;
+        }
+
+        public Builder required(final boolean required) {
+            this.required = required;
+            return this;
+        }
+
+        public UserSearchPanel build() {
+            return new UserSearchPanel(this);
+        }
+    }
+
+    private UserSearchPanel(final Builder builder) {
+        super(builder.id, AttributableType.USER, builder.fiql, builder.required);
+    }
+
+    @Override
+    protected void populate() {
+        super.populate();
+
+        this.types = new LoadableDetachableModel<List<SearchClause.Type>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<SearchClause.Type> load() {
+                List<SearchClause.Type> result = new ArrayList<SearchClause.Type>();
+                result.add(SearchClause.Type.ATTRIBUTE);
+                result.add(SearchClause.Type.MEMBERSHIP);
+                result.add(SearchClause.Type.RESOURCE);
+                return result;
+            }
+        };
+
+        this.roleNames = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                List<RoleTO> roleTOs = roleRestClient.list();
+
+                List<String> result = new ArrayList<String>(roleTOs.size());
+                for (RoleTO role : roleTOs) {
+                    result.add(role.getDisplayName());
+                }
+
+                return result;
+            }
+        };
+    }
+
+    @Override
+    protected SyncopeFiqlSearchConditionBuilder getSearchConditionBuilder() {
+        return SyncopeClient.getUserSearchConditionBuilder();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
new file mode 100644
index 0000000..7e494a5
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/UserSearchResultPanel.java
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.DisplayAttributesModalPage;
+import org.apache.syncope.client.console.pages.EditUserModalPage;
+import org.apache.syncope.client.console.pages.ResultStatusModalPage;
+import org.apache.syncope.client.console.pages.StatusModalPage;
+import org.apache.syncope.client.console.rest.AbstractSubjectRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.rest.UserRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.ActionColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.AttrColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.DatePropertyColumn;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.TokenColumn;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink.ActionType;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksPanel;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.Page;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.springframework.util.ReflectionUtils;
+
+public class UserSearchResultPanel extends AbstractSearchResultPanel {
+
+    private static final long serialVersionUID = -905187144506842332L;
+
+    private final static String PAGEID = "Users";
+
+    @SpringBean
+    private SchemaRestClient schemaRestClient;
+
+    private final List<String> pSchemaNames;
+
+    private final List<String> dSchemaNames;
+
+    private final List<String> vSchemaNames;
+
+    public <T extends AbstractAttributableTO> UserSearchResultPanel(final String id, final boolean filtered,
+            final String fiql, final PageReference callerRef, final AbstractSubjectRestClient restClient) {
+
+        super(id, filtered, fiql, callerRef, restClient);
+
+        this.pSchemaNames = schemaRestClient.getSchemaNames(AttributableType.USER);
+        this.dSchemaNames = schemaRestClient.getDerSchemaNames(AttributableType.USER);
+        this.vSchemaNames = schemaRestClient.getVirSchemaNames(AttributableType.USER);
+
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<AbstractAttributableTO, String>> getColumns() {
+        final List<IColumn<AbstractAttributableTO, String>> columns =
+                new ArrayList<IColumn<AbstractAttributableTO, String>>();
+
+        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_DETAILS_VIEW)) {
+            final Field field = ReflectionUtils.findField(UserTO.class, name);
+
+            if ("token".equalsIgnoreCase(name)) {
+                columns.add(new TokenColumn("token"));
+            } else if (field != null && field.getType().equals(Date.class)) {
+                columns.add(new DatePropertyColumn<AbstractAttributableTO>(new ResourceModel(name, name), name, name));
+            } else {
+                columns.add(
+                        new PropertyColumn<AbstractAttributableTO, String>(new ResourceModel(name, name), name, name));
+            }
+        }
+
+        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_ATTRIBUTES_VIEW)) {
+            if (pSchemaNames.contains(name)) {
+                columns.add(new AttrColumn(name, SchemaType.PLAIN));
+            }
+        }
+
+        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_DERIVED_ATTRIBUTES_VIEW)) {
+            if (dSchemaNames.contains(name)) {
+                columns.add(new AttrColumn(name, SchemaType.DERIVED));
+            }
+        }
+
+        for (String name : prefMan.getList(getRequest(), Constants.PREF_USERS_VIRTUAL_ATTRIBUTES_VIEW)) {
+            if (vSchemaNames.contains(name)) {
+                columns.add(new AttrColumn(name, SchemaType.VIRTUAL));
+            }
+        }
+
+        // Add defaults in case of no selection
+        if (columns.isEmpty()) {
+            for (String name : DisplayAttributesModalPage.DEFAULT_SELECTION) {
+                columns.add(
+                        new PropertyColumn<AbstractAttributableTO, String>(new ResourceModel(name, name), name, name));
+            }
+
+            prefMan.setList(getRequest(), getResponse(), Constants.PREF_USERS_DETAILS_VIEW,
+                    Arrays.asList(DisplayAttributesModalPage.DEFAULT_SELECTION));
+        }
+
+        columns.add(new ActionColumn<AbstractAttributableTO, String>(new ResourceModel("actions", "")) {
+
+            private static final long serialVersionUID = -3503023501954863131L;
+
+            @Override
+            public ActionLinksPanel getActions(final String componentId, final IModel<AbstractAttributableTO> model) {
+
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, model, page.getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        statusmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new StatusModalPage<UserTO>(
+                                        page.getPageReference(), statusmodal, (UserTO) model.getObject());
+                            }
+                        });
+
+                        statusmodal.show(target);
+                    }
+                }, ActionLink.ActionType.MANAGE_RESOURCES, PAGEID);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        statusmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new StatusModalPage<UserTO>(
+                                        page.getPageReference(), statusmodal, (UserTO) model.getObject(), true);
+                            }
+                        });
+
+                        statusmodal.show(target);
+                    }
+                }, ActionLink.ActionType.ENABLE, PAGEID);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        editmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                // SYNCOPE-294: re-read userTO before edit
+                                UserTO userTO = ((UserRestClient) restClient).read(model.getObject().getKey());
+                                return new EditUserModalPage(page.getPageReference(), editmodal, userTO);
+                            }
+                        });
+
+                        editmodal.show(target);
+                    }
+                }, ActionLink.ActionType.EDIT, PAGEID);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        try {
+                            final UserTO userTO = (UserTO) restClient.
+                                    delete(model.getObject().getETagValue(), model.getObject().getKey());
+
+                            page.setModalResult(true);
+
+                            editmodal.setPageCreator(new ModalWindow.PageCreator() {
+
+                                private static final long serialVersionUID = -7834632442532690940L;
+
+                                @Override
+                                public Page createPage() {
+                                    return new ResultStatusModalPage.Builder(editmodal, userTO).build();
+                                }
+                            });
+
+                            editmodal.show(target);
+                        } catch (SyncopeClientException scce) {
+                            error(getString(Constants.OPERATION_ERROR) + ": " + scce.getMessage());
+                            feedbackPanel.refresh(target);
+                        }
+                    }
+                }, ActionLink.ActionType.DELETE, PAGEID);
+
+                return panel;
+            }
+
+            @Override
+            public ActionLinksPanel getHeader(final String componentId) {
+                final ActionLinksPanel panel = new ActionLinksPanel(componentId, new Model(), page.getPageReference());
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        displaymodal.setPageCreator(new ModalWindow.PageCreator() {
+
+                            private static final long serialVersionUID = -7834632442532690940L;
+
+                            @Override
+                            public Page createPage() {
+                                return new DisplayAttributesModalPage(page.getPageReference(), displaymodal,
+                                        pSchemaNames, dSchemaNames, vSchemaNames);
+                            }
+                        });
+
+                        displaymodal.show(target);
+                    }
+                }, ActionLink.ActionType.CHANGE_VIEW, PAGEID);
+
+                panel.add(new ActionLink() {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        if (target != null) {
+                            target.add(container);
+                        }
+                    }
+                }, ActionLink.ActionType.RELOAD, PAGEID, "list");
+
+                return panel;
+            }
+        });
+
+        return columns;
+    }
+
+    @Override
+    protected <T extends AbstractAttributableTO> Collection<ActionType> getBulkActions() {
+        final List<ActionType> bulkActions = new ArrayList<ActionType>();
+
+        bulkActions.add(ActionType.DELETE);
+        bulkActions.add(ActionType.SUSPEND);
+        bulkActions.add(ActionType.REACTIVATE);
+
+        return bulkActions;
+    }
+
+    @Override
+    protected String getPageId() {
+        return PAGEID;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/VirAttrsPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/VirAttrsPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/VirAttrsPanel.java
new file mode 100644
index 0000000..34d21d5
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/panels/VirAttrsPanel.java
@@ -0,0 +1,295 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.panels;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.panels.AttrTemplatesPanel.RoleAttrTemplatesChange;
+import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDecoratedCheckbox;
+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.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.attributes.AjaxCallListener;
+import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.ChoiceRenderer;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+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.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class VirAttrsPanel extends Panel {
+
+    private static final long serialVersionUID = -7982691107029848579L;
+
+    @SpringBean
+    private SchemaRestClient schemaRestClient;
+
+    @SpringBean
+    private RoleRestClient roleRestClient;
+
+    private final AttrTemplatesPanel attrTemplates;
+
+    private final Map<String, VirSchemaTO> schemas = new TreeMap<String, VirSchemaTO>();
+
+    public <T extends AbstractAttributableTO> VirAttrsPanel(final String id, final T entityTO,
+            final boolean templateMode) {
+
+        this(id, entityTO, templateMode, null);
+    }
+
+    public <T extends AbstractAttributableTO> VirAttrsPanel(final String id, final T entityTO,
+            final boolean templateMode, final AttrTemplatesPanel attrTemplates) {
+
+        super(id);
+        this.attrTemplates = attrTemplates;
+        this.setOutputMarkupId(true);
+
+        final IModel<List<String>> virSchemas = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            private void filter(final List<VirSchemaTO> schemaTOs, final Set<String> allowed) {
+                for (ListIterator<VirSchemaTO> itor = schemaTOs.listIterator(); itor.hasNext();) {
+                    VirSchemaTO schema = itor.next();
+                    if (!allowed.contains(schema.getKey())) {
+                        itor.remove();
+                    }
+                }
+            }
+
+            @Override
+            protected List<String> load() {
+                List<VirSchemaTO> schemaTOs;
+
+                if (entityTO instanceof RoleTO) {
+                    final RoleTO roleTO = (RoleTO) entityTO;
+
+                    schemaTOs = schemaRestClient.getVirSchemas(AttributableType.ROLE);
+                    Set<String> allowed;
+                    if (attrTemplates == null) {
+                        allowed = new HashSet<String>(roleTO.getRVirAttrTemplates());
+                    } else {
+                        allowed = new HashSet<String>(attrTemplates.getSelected(
+                                AttrTemplatesPanel.Type.rVirAttrTemplates));
+                        if (roleTO.isInheritTemplates() && roleTO.getParent() != 0) {
+                            allowed.addAll(roleRestClient.read(roleTO.getParent()).getRVirAttrTemplates());
+                        }
+                    }
+                    filter(schemaTOs, allowed);
+                } else if (entityTO instanceof UserTO) {
+                    schemaTOs = schemaRestClient.getVirSchemas(AttributableType.USER);
+                } else {
+                    schemaTOs = schemaRestClient.getVirSchemas(AttributableType.MEMBERSHIP);
+                    Set<String> allowed = new HashSet<String>(
+                            roleRestClient.read(((MembershipTO) entityTO).getRoleId()).getMVirAttrTemplates());
+                    filter(schemaTOs, allowed);
+                }
+
+                schemas.clear();
+
+                for (VirSchemaTO schemaTO : schemaTOs) {
+                    schemas.put(schemaTO.getKey(), schemaTO);
+                }
+
+                return new ArrayList<>(schemas.keySet());
+            }
+        };
+
+        final WebMarkupContainer attributesContainer = new WebMarkupContainer("virAttrContainer");
+        attributesContainer.setOutputMarkupId(true);
+        add(attributesContainer);
+
+        AjaxButton addAttributeBtn = new IndicatingAjaxButton("addAttributeBtn", new ResourceModel("addAttributeBtn")) {
+
+            private static final long serialVersionUID = -4804368561204623354L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+                entityTO.getVirAttrs().add(new AttrTO());
+                target.add(attributesContainer);
+            }
+
+            @Override
+            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                target.add(attributesContainer);
+            }
+        };
+
+        add(addAttributeBtn.setDefaultFormProcessing(Boolean.FALSE));
+
+        ListView<AttrTO> attributes = new ListView<AttrTO>("attrs",
+                new PropertyModel<List<? extends AttrTO>>(entityTO, "virAttrs")) {
+
+                    private static final long serialVersionUID = 9101744072914090143L;
+
+                    @Override
+                    @SuppressWarnings("unchecked")
+                    protected void populateItem(final ListItem<AttrTO> item) {
+                        final AttrTO attributeTO = item.getModelObject();
+
+                        item.add(new AjaxDecoratedCheckbox("toRemove", new Model<Boolean>(Boolean.FALSE)) {
+
+                            private static final long serialVersionUID = 7170946748485726506L;
+
+                            @Override
+                            protected void onUpdate(final AjaxRequestTarget target) {
+                                entityTO.getVirAttrs().remove(attributeTO);
+                                target.add(attributesContainer);
+                            }
+
+                            @Override
+                            protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
+                                super.updateAjaxAttributes(attributes);
+
+                                final AjaxCallListener ajaxCallListener = new AjaxCallListener() {
+
+                                    private static final long serialVersionUID = 7160235486520935153L;
+
+                                    @Override
+                                    public CharSequence getPrecondition(final Component component) {
+                                        return "if (!confirm('" + getString("confirmDelete") + "')) return false;";
+                                    }
+                                };
+                                attributes.getAjaxCallListeners().add(ajaxCallListener);
+                            }
+                        });
+
+                        if (attributeTO.getValues().isEmpty()) {
+                            attributeTO.getValues().add("");
+                        }
+
+                        if (attributeTO.getSchema() != null) {
+                            VirSchemaTO attributeSchema = schemas.get(attributeTO.getSchema());
+                            if (attributeSchema != null) {
+                                attributeTO.setReadonly(attributeSchema.isReadonly());
+                            }
+                        }
+
+                        final AjaxTextFieldPanel panel;
+                        final MultiFieldPanel multiPanel;
+                        if (templateMode) {
+                            panel = new AjaxTextFieldPanel("values", "values", new Model<String>());
+                            panel.setReadOnly(attributeTO.isReadonly());
+                            multiPanel = null;
+                        } else {
+                            panel = new AjaxTextFieldPanel("panel", "values", new Model<String>(null));
+                            panel.setReadOnly(attributeTO.isReadonly());
+                            multiPanel = new MultiFieldPanel("values",
+                                    new PropertyModel<List<String>>(attributeTO, "values"), panel);
+                        }
+
+                        final DropDownChoice<String> schemaChoice = new DropDownChoice<String>("schema",
+                                new PropertyModel<String>(attributeTO, "schema"), virSchemas,
+                                new ChoiceRenderer<String>() {
+
+                                    private static final long serialVersionUID = 3109256773218160485L;
+
+                                    @Override
+                                    public Object getDisplayValue(final String object) {
+                                        final StringBuilder text = new StringBuilder(object);
+                                        if (templateMode) {
+                                            text.append(" (JEXL)");
+                                        }
+                                        return text.toString();
+                                    }
+                                });
+
+                        schemaChoice.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
+
+                            private static final long serialVersionUID = -1107858522700306810L;
+
+                            @Override
+                            protected void onUpdate(final AjaxRequestTarget target) {
+                                attributeTO.setSchema(schemaChoice.getModelObject());
+
+                                VirSchemaTO virSchema = schemas.get(attributeTO.getSchema());
+                                if (virSchema != null) {
+                                    attributeTO.setReadonly(virSchema.isReadonly());
+                                    panel.setReadOnly(attributeTO.isReadonly());
+                                }
+
+                                if (multiPanel != null) {
+                                    multiPanel.getView().setEnabled(false);
+                                }
+                            }
+                        });
+
+                        schemaChoice.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                            private static final long serialVersionUID = -1107858522700306810L;
+
+                            @Override
+                            protected void onUpdate(final AjaxRequestTarget target) {
+                                target.add(attributesContainer);
+                            }
+                        });
+
+                        schemaChoice.setOutputMarkupId(true);
+                        schemaChoice.setRequired(true);
+                        item.add(schemaChoice);
+
+                        if (templateMode) {
+                            item.add(panel);
+                        } else {
+                            item.add(multiPanel);
+                        }
+                    }
+                };
+
+        attributesContainer.add(attributes);
+    }
+
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        if ((event.getPayload() instanceof RoleAttrTemplatesChange)) {
+            final RoleAttrTemplatesChange update = (RoleAttrTemplatesChange) event.getPayload();
+            if (attrTemplates != null && update.getType() == AttrTemplatesPanel.Type.rVirAttrTemplates) {
+                update.getTarget().add(this);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
new file mode 100644
index 0000000..bc5998a
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/BinaryPreview.java
@@ -0,0 +1,34 @@
+/*
+ * 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.preview;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface BinaryPreview {
+
+    public String[] mimeTypes() default {};
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
new file mode 100644
index 0000000..2baff26
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewUtil.java
@@ -0,0 +1,67 @@
+/*
+ * 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.preview;
+
+import java.lang.reflect.InvocationTargetException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.init.PreviewPanelClassInitializer;
+import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
+import org.apache.wicket.Component;
+import org.apache.wicket.util.crypt.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+@org.springframework.stereotype.Component
+public class PreviewUtil {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(PreviewUtil.class);
+
+    @Autowired
+    private PreviewPanelClassInitializer previewPanelClassInitializer;
+
+    public Component getPreviewer(final String mimeType, final String file) throws ClassNotFoundException,
+            NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
+
+        final Class<? extends AbstractBinaryPreviewer> previewer = StringUtils.isBlank(file)
+                ? null
+                : previewPanelClassInitializer.getClass(mimeType);
+
+        return previewer == null
+                ? null
+                : ((AbstractBinaryPreviewer) Class.forName(previewer.getName()).
+                getConstructor(String.class, String.class, byte[].class).newInstance(
+                        new Object[] { "previewer", mimeType, Base64.decodeBase64(file) })).preview();
+    }
+
+    public Component getPreviewer(final String mimeType, final byte[] file) throws ClassNotFoundException,
+            NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
+
+        final Class<? extends AbstractBinaryPreviewer> previewer = previewPanelClassInitializer.getClass(mimeType);
+
+        return previewer == null
+                ? null
+                : ((AbstractBinaryPreviewer) Class.forName(previewer.getName()).
+                getConstructor(String.class, String.class, byte[].class).newInstance(
+                        new Object[] { "previewer", mimeType, file })).preview();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
new file mode 100644
index 0000000..921d783
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/preview/PreviewerClassScanner.java
@@ -0,0 +1,50 @@
+/*
+ * 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.preview;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.client.console.wicket.markup.html.form.preview.AbstractBinaryPreviewer;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+@Component
+public class PreviewerClassScanner extends ClassPathScanningCandidateComponentProvider {
+
+    private static final String BASE_PATH = "org.apache.syncope.console.wicket.markup.html.form";
+
+    public PreviewerClassScanner() {
+        super(false);
+        addIncludeFilter(new AnnotationTypeFilter(BinaryPreview.class));
+    }
+
+    @SuppressWarnings("unchecked")
+    public final List<Class<? extends AbstractBinaryPreviewer>> getComponentClasses() {
+        List<Class<? extends AbstractBinaryPreviewer>> classes =
+                new ArrayList<Class<? extends AbstractBinaryPreviewer>>();
+        for (BeanDefinition candidate : findCandidateComponents(BASE_PATH)) {
+            classes.add((Class<AbstractBinaryPreviewer>) ClassUtils.resolveClassName(candidate.getBeanClassName(),
+                    ClassUtils.getDefaultClassLoader()));
+        }
+        return classes;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/FilesystemResource.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/FilesystemResource.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/FilesystemResource.java
new file mode 100644
index 0000000..8a95de6
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/FilesystemResource.java
@@ -0,0 +1,83 @@
+/*
+ * 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.resources;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Mounts directory on local filesystem as subcontext.
+ */
+public class FilesystemResource extends AbstractResource {
+
+    private static final long serialVersionUID = -4791087117785935198L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(FilesystemResource.class);
+
+    private final String baseCtx;
+
+    private final String basePath;
+
+    public FilesystemResource(final String baseCtx, final String basePath) {
+        this.baseCtx = baseCtx;
+        this.basePath = basePath;
+    }
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        ResourceResponse response = new ResourceResponse();
+
+        final File baseDir = new File(basePath);
+        if (baseDir.exists() && baseDir.canRead() && baseDir.isDirectory()) {
+            String reqPath = attributes.getRequest().getUrl().getPath();
+            final String subPath = reqPath.substring(reqPath.indexOf(baseCtx) + baseCtx.length()).
+                    replace('/', File.separatorChar);
+            LOG.debug("Request for {}", subPath);
+
+            response.setWriteCallback(new WriteCallback() {
+
+                @Override
+                public void writeData(final Attributes attributes) throws IOException {
+                    FileInputStream resourceIS = null;
+                    try {
+                        resourceIS = new FileInputStream(new File(baseDir, subPath));
+                        IOUtils.copy(resourceIS, attributes.getResponse().getOutputStream());
+                    } catch (IOException e) {
+                        LOG.error("Could not read from {}", baseDir.getAbsolutePath() + subPath, e);
+                    } finally {
+                        IOUtils.closeQuietly(resourceIS);
+                    }
+                }
+            });
+        } else {
+            LOG.error("{} not found, not readable or not a directory", basePath);
+        }
+
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefGETResource.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefGETResource.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefGETResource.java
new file mode 100644
index 0000000..2104745
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefGETResource.java
@@ -0,0 +1,58 @@
+/*
+ * 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.resources;
+
+import java.io.IOException;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.client.console.rest.WorkflowRestClient;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.io.IOUtils;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Mirror REST resource for obtaining user workflow definition in JSON (used by Activiti Modeler).
+ *
+ * @see org.apache.syncope.common.services.WorkflowService#exportDefinition(org.apache.syncope.common.types.SubjectType)
+ */
+public class WorkflowDefGETResource extends AbstractResource {
+
+    private static final long serialVersionUID = 4637304868056148970L;
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        ResourceResponse response = new ResourceResponse();
+        response.disableCaching();
+        response.setContentType(MediaType.APPLICATION_JSON);
+
+        response.setWriteCallback(new WriteCallback() {
+
+            @Override
+            public void writeData(final Attributes attributes) throws IOException {
+                IOUtils.copy(WebApplicationContextUtils.getWebApplicationContext(
+                        WebApplication.get().getServletContext()).getBean(WorkflowRestClient.class).
+                        getDefinition(MediaType.APPLICATION_JSON_TYPE),
+                        attributes.getResponse().getOutputStream());
+            }
+        });
+
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefPUTResource.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefPUTResource.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefPUTResource.java
new file mode 100644
index 0000000..add6485
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/resources/WorkflowDefPUTResource.java
@@ -0,0 +1,74 @@
+/*
+ * 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.resources;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.MediaType;
+import org.apache.cxf.common.util.UrlUtils;
+import org.apache.syncope.client.console.rest.WorkflowRestClient;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.request.resource.AbstractResource;
+import org.apache.wicket.util.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Mirror REST resource for putting user workflow definition in JSON (used by Activiti Modeler).
+ *
+ * @see org.apache.syncope.common.services.WorkflowService#importDefinition(
+ * org.apache.syncope.common.types.SubjectType, java.lang.String)
+ */
+public class WorkflowDefPUTResource extends AbstractResource {
+
+    private static final long serialVersionUID = 2964542005207297944L;
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(WorkflowDefPUTResource.class);
+
+    @Override
+    protected ResourceResponse newResourceResponse(final Attributes attributes) {
+        String definition = null;
+        try {
+            HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
+            String requestBody = IOUtils.toString(request.getInputStream());
+            String[] split = requestBody.split("&");
+            for (int i = 0; i < split.length && definition == null; i++) {
+                String keyValue = split[i];
+                if (keyValue.startsWith("json_xml=")) {
+                    definition = UrlUtils.urlDecode(keyValue.split("=")[1]);
+                }
+            }
+        } catch (IOException e) {
+            LOG.error("Could not extract workflow definition from request", e);
+        }
+
+        WebApplicationContextUtils.getWebApplicationContext(WebApplication.get().getServletContext()).
+                getBean(WorkflowRestClient.class).
+                updateDefinition(MediaType.APPLICATION_JSON_TYPE, definition);
+
+        ResourceResponse response = new ResourceResponse();
+        response.setStatusCode(204);
+        return response;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AbstractSubjectRestClient.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AbstractSubjectRestClient.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AbstractSubjectRestClient.java
new file mode 100644
index 0000000..a18a0a9
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AbstractSubjectRestClient.java
@@ -0,0 +1,46 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
+
+public abstract class AbstractSubjectRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = 1962529678091410544L;
+
+    public abstract int count();
+
+    public abstract List<? extends AbstractAttributableTO> list(int page, int size, final SortParam<String> sort);
+
+    public abstract int searchCount(String fiql);
+
+    public abstract List<? extends AbstractAttributableTO> search(String fiql,
+            int page, int size, final SortParam<String> sort);
+
+    public abstract ConnObjectTO getConnectorObject(String resourceName, Long id);
+
+    public abstract AbstractAttributableTO delete(String etag, Long id);
+
+    public abstract BulkActionResult bulkAction(BulkAction action);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/ApprovalRestClient.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/ApprovalRestClient.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/ApprovalRestClient.java
new file mode 100644
index 0000000..dc9d12f
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/ApprovalRestClient.java
@@ -0,0 +1,45 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.WorkflowFormTO;
+import org.apache.syncope.common.rest.api.service.UserWorkflowService;
+import org.springframework.stereotype.Component;
+
+/**
+ * Console client for invoking Rest Todo services.
+ */
+@Component
+public class ApprovalRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -4785231164900813921L;
+
+    public List<WorkflowFormTO> getForms() {
+        return getService(UserWorkflowService.class).getForms();
+    }
+
+    public WorkflowFormTO claimForm(final String taskId) {
+        return getService(UserWorkflowService.class).claimForm(taskId);
+    }
+
+    public void submitForm(final WorkflowFormTO form) {
+        getService(UserWorkflowService.class).submitForm(form);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AuthRestClient.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AuthRestClient.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AuthRestClient.java
new file mode 100644
index 0000000..01e6838
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/rest/AuthRestClient.java
@@ -0,0 +1,44 @@
+/*
+ * 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.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.rest.api.CollectionWrapper;
+import org.apache.syncope.common.rest.api.service.EntitlementService;
+import org.springframework.stereotype.Component;
+
+/**
+ * Console client for invoking Rest Resources services.
+ */
+@Component
+public class AuthRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = 2999780105004742914L;
+
+    public List<String> getAllEntitlements() {
+        return new ArrayList<>(
+                CollectionWrapper.unwrap(getService(EntitlementService.class).getAllEntitlements()));
+    }
+
+    public List<String> getOwnedEntitlements() {
+        return new ArrayList<>(
+                CollectionWrapper.unwrap(getService(EntitlementService.class).getOwnEntitlements()));
+    }
+}