syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmarte...@apache.org
Subject [25/28] syncope git commit: provides wizard to create users, groups and any objects + several changes merged from master
Date Fri, 30 Oct 2015 11:42:32 GMT
http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
index 2b3c49d..e030ef7 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.List;
 import org.apache.syncope.client.console.commons.Constants;
 import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.panels.ModalPanel;
 import org.apache.syncope.client.console.panels.NotificationPanel;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.buttons.DefaultModalCloseButton;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.buttons.PrimaryModalButton;
@@ -39,6 +40,7 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 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.CompoundPropertyModel;
 import org.apache.wicket.model.IModel;
 import org.slf4j.Logger;
@@ -63,7 +65,7 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
 
     private WindowClosedCallback windowClosedCallback;
 
-    private AbstractModalPanel content;
+    private Panel content;
 
     private PrimaryModalButton submitButton;
 
@@ -127,11 +129,19 @@ public class BaseModal<T extends Serializable> extends Modal<T> {
         return form.getModelObject();
     }
 
-    public AbstractModalPanel getContent() {
-        return content;
+    public ModalPanel getContent() {
+        return ModalPanel.class.cast(content);
     }
 
-    public BaseModal<T> setContent(final AbstractModalPanel component) {
+    public BaseModal<T> setContent(final ModalPanel component) {
+        if (component instanceof Panel) {
+            return setInternalContent(Panel.class.cast(component));
+        }
+        throw new IllegalArgumentException("Panel instance is required");
+
+    }
+
+    private BaseModal<T> setInternalContent(final Panel component) {
         if (!component.getId().equals(getContentId())) {
             throw new WicketRuntimeException(
                     "Modal content id is wrong. Component ID:" + component.getId() + "; content ID: " + getContentId());

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
index 8ad55f6..fda21bf 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksPanel.java
@@ -53,6 +53,8 @@ public final class ActionLinksPanel<T extends Serializable> extends Panel {
         this.model = model;
         this.pageRef = pageRef;
 
+        setOutputMarkupId(true);
+        
         super.add(new Fragment("panelClaim", "emptyFragment", this));
         super.add(new Fragment("panelManageResources", "emptyFragment", this));
         super.add(new Fragment("panelManageUsers", "emptyFragment", this));

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
index b7a524a..1311980 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.client.console.wicket.markup.html.form;
 
 import java.io.ByteArrayInputStream;
+import java.lang.reflect.InvocationTargetException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
@@ -61,7 +62,7 @@ public class BinaryFieldPanel extends FieldPanel<String> {
 
     private final Fragment emptyFragment;
 
-    private final PreviewUtils previewUtils = PreviewUtils.getInstance();
+    private final transient PreviewUtils previewUtils = PreviewUtils.getInstance();
 
     public BinaryFieldPanel(final String id, final String name, final IModel<String> model, final String mimeType) {
         super(id, model);
@@ -135,7 +136,7 @@ public class BinaryFieldPanel extends FieldPanel<String> {
                         uploadForm.addOrReplace(fileUpload);
                         downloadLink.setEnabled(StringUtils.isNotBlank(uploaded));
                         target.add(uploadForm);
-                    } catch (Exception e) {
+                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                         error(getString(Constants.ERROR) + ": " + e.getMessage());
                         ((BasePage) getPage()).getFeedbackPanel().refresh(target);
                         LOG.error("While saving uploaded file", e);
@@ -198,7 +199,7 @@ public class BinaryFieldPanel extends FieldPanel<String> {
             if (panelPreview != null) {
                 changePreviewer(panelPreview);
             }
-        } catch (Exception e) {
+        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
             LOG.error("While loading saved file", e);
         }
         downloadLink.setEnabled(StringUtils.isNotBlank(model.getObject()));

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
index 3370cd7..3d923c6 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
@@ -67,8 +67,8 @@ public class SpinnerFieldPanel<T extends Number> extends FieldPanel<T> {
     }
 
     private void init(final String name, final Class<T> reference, final IModel<T> model, final SpinnerConfig conf) {
-        final Spinner<T> spinner = new Spinner<>("spinner", model, conf);
-        add(spinner);
+        field = new Spinner<>("spinner", model, conf);
+        add(field);
 
         this.name = name;
         this.model = model;

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
index caa3857..1a1cdc5 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizard.java
@@ -16,6 +16,7 @@
 package org.apache.syncope.client.console.wizards;
 
 import java.io.Serializable;
+import org.apache.syncope.client.console.panels.ModalPanel;
 import org.apache.syncope.client.console.panels.NotificationPanel;
 import org.apache.wicket.Component;
 import org.apache.wicket.PageReference;
@@ -23,13 +24,18 @@ import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.extensions.wizard.Wizard;
 import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-public abstract class AjaxWizard<T extends Serializable> extends Wizard {
+public abstract class AjaxWizard<T extends Serializable> extends Wizard implements ModalPanel {
 
     private static final long serialVersionUID = 1L;
 
+    protected static final Logger LOG = LoggerFactory.getLogger(AjaxWizard.class);
+
     private final PageReference pageRef;
 
     private T item;
@@ -87,9 +93,15 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard {
      */
     @Override
     public final void onCancel() {
-        onCancelInternal();
-        send(pageRef.getPage(), Broadcast.DEPTH,
-                new NewItemCancelEvent<T>(item, RequestCycle.get().find(AjaxRequestTarget.class)));
+        final AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class);
+        try {
+            onCancelInternal();
+            send(pageRef.getPage(), Broadcast.DEPTH, new NewItemCancelEvent<T>(item, target));
+        } catch (Exception e) {
+            LOG.warn("Wizard error on cancel", e);
+            error(getString("wizard.cancel.error"));
+            feedbackPanel.refresh(target);
+        }
     }
 
     /**
@@ -97,9 +109,15 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard {
      */
     @Override
     public final void onFinish() {
-        onApplyInternal();
-        send(pageRef.getPage(), Broadcast.DEPTH,
-                new NewItemFinishEvent<T>(item, RequestCycle.get().find(AjaxRequestTarget.class)));
+        final AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class);
+        try {
+            onApplyInternal();
+            send(pageRef.getPage(), Broadcast.DEPTH, new NewItemFinishEvent<T>(item, target));
+        } catch (Exception e) {
+            LOG.warn("Wizard error on finish", e);
+            error(getString("wizard.apply.error"));
+            feedbackPanel.refresh(target);
+        }
     }
 
     public T getItem() {
@@ -171,4 +189,14 @@ public abstract class AjaxWizard<T extends Serializable> extends Wizard {
         }
 
     }
+
+    @Override
+    public void onSubmit(final AjaxRequestTarget target, final Form<?> form) {
+        onApplyInternal();
+    }
+
+    @Override
+    public void onError(final AjaxRequestTarget target, final Form<?> form) {
+        feedbackPanel.refresh(target);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
index 035eab6..7c778e7 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardBuilder.java
@@ -28,7 +28,7 @@ public abstract class AjaxWizardBuilder<T extends Serializable> {
 
     private final PageReference pageRef;
 
-    private final T defaultItem;
+    private T defaultItem;
 
     private T item;
 
@@ -55,7 +55,7 @@ public abstract class AjaxWizardBuilder<T extends Serializable> {
 
     public AjaxWizard<T> build(final boolean edit) {
         final T modelObject = getItem();
-        setItem(null);
+        this.item = null;
 
         return new AjaxWizard<T>(id, modelObject, buildModelSteps(modelObject, new WizardModel()), pageRef, edit) {
 
@@ -63,34 +63,39 @@ public abstract class AjaxWizardBuilder<T extends Serializable> {
 
             @Override
             protected void onCancelInternal() {
-                AjaxWizardBuilder.this.onCancelInternal();
+                AjaxWizardBuilder.this.onCancelInternal(getItem());
             }
 
             @Override
             protected void onApplyInternal() {
-                AjaxWizardBuilder.this.onApplyInternal();
+                AjaxWizardBuilder.this.onApplyInternal(getItem());
             }
         };
     }
 
     protected abstract WizardModel buildModelSteps(final T modelObject, final WizardModel wizardModel);
 
-    protected abstract void onCancelInternal();
+    protected abstract void onCancelInternal(T modelObject);
 
-    protected abstract void onApplyInternal();
+    protected abstract void onApplyInternal(T modelObject);
+
+    protected T getDefaultItem() {
+        return defaultItem;
+    }
 
     private T getItem() {
         return item == null ? SerializationUtils.clone(defaultItem) : item;
     }
 
     /**
-     * Replaces the default value provided with the constructor.
+     * Replaces the default value provided with the constructor and nullify working item object.
      *
      * @param item new value.
      * @return the current wizard factory instance.
      */
     public AjaxWizardBuilder<T> setItem(final T item) {
-        this.item = item;
+        this.defaultItem = item;
+        this.item = null;
         return this;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardButtonBar.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardButtonBar.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardButtonBar.java
index b272ff8..7ceb2fd 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardButtonBar.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/AjaxWizardButtonBar.java
@@ -71,6 +71,7 @@ public class AjaxWizardButtonBar extends WizardButtonBar {
             @Override
             protected void onClick(final AjaxRequestTarget target, final Form<?> form) {
                 getWizardModel().previous();
+                wizard.modelChanged();
                 target.add(wizard);
             }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
new file mode 100644
index 0000000..1e02025
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/WizardMgtPanel.java
@@ -0,0 +1,216 @@
+/*
+ * 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.wizards;
+
+import java.io.Serializable;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.panels.NotificationPanel;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.event.IEventSource;
+import org.apache.wicket.markup.html.TransparentWebMarkupContainer;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.ResourceModel;
+
+public abstract class WizardMgtPanel<T extends Serializable> extends Panel implements IEventSource {
+
+    private static final long serialVersionUID = 1L;
+
+    private final WebMarkupContainer container;
+
+    private final Fragment initialFragment;
+
+    private final AjaxLink<?> addAjaxLink;
+
+    private AjaxWizardBuilder<T> newItemPanelBuilder;
+
+    private NotificationPanel notificationPanel;
+
+    private final PageReference pageRef;
+
+    /**
+     * Modal window.
+     */
+    protected final BaseModal<T> modal = new BaseModal<T>("modal") {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void onConfigure() {
+            super.onConfigure();
+            setFooterVisible(false);
+        }
+
+    };
+
+    private final boolean wizardInModal;
+
+    public WizardMgtPanel(final String id, final PageReference pageRef) {
+        this(id, pageRef, false);
+    }
+
+    public WizardMgtPanel(final String id, final PageReference pageRef, final boolean wizardInModal) {
+        super(id);
+        setOutputMarkupId(true);
+        this.pageRef = pageRef;
+        this.wizardInModal = wizardInModal;
+
+        add(modal);
+
+        container = new TransparentWebMarkupContainer("container");
+        container.setOutputMarkupPlaceholderTag(true).setOutputMarkupId(true);
+        add(container);
+
+        initialFragment = new Fragment("content", "default", this);
+        container.addOrReplace(initialFragment);
+
+        addAjaxLink = new AjaxLink<T>("add") {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                send(WizardMgtPanel.this, Broadcast.BREADTH, new AjaxWizard.NewItemActionEvent<T>(null, target));
+            }
+        };
+
+        addAjaxLink.setEnabled(false);
+        addAjaxLink.setVisible(false);
+        initialFragment.add(addAjaxLink);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onEvent(final IEvent<?> event) {
+        if (event.getPayload() instanceof AjaxWizard.NewItemEvent) {
+            final AjaxRequestTarget target = AjaxWizard.NewItemEvent.class.cast(event.getPayload()).getTarget();
+
+            final T item = ((AjaxWizard.NewItemEvent<T>) event.getPayload()).getItem();
+
+            if (event.getPayload() instanceof AjaxWizard.NewItemActionEvent) {
+                if (item != null) {
+                    newItemPanelBuilder.setItem(item);
+                }
+
+                final AjaxWizard<T> wizard = newItemPanelBuilder.build(
+                        ((AjaxWizard.NewItemActionEvent<T>) event.getPayload()).getIndex());
+
+                if (wizardInModal) {
+                    final IModel<T> model = new CompoundPropertyModel<>(item);
+                    modal.setFormModel(model);
+
+                    target.add(modal.setContent(wizard));
+
+                    modal.header(new ResourceModel("item.new", "New item"));
+                    modal.show(true);
+                } else {
+                    final Fragment fragment = new Fragment("content", "wizard", WizardMgtPanel.this);
+                    fragment.add(wizard);
+                    container.addOrReplace(fragment);
+                }
+            } else {
+                if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
+                    if (notificationPanel != null) {
+                        getSession().info(getString(Constants.OPERATION_SUCCEEDED));
+                        notificationPanel.refresh(target);
+                    }
+                }
+
+                if (wizardInModal) {
+                    modal.show(false);
+                    modal.close(target);
+                } else {
+                    container.addOrReplace(initialFragment);
+                }
+            }
+
+            target.add(container);
+        }
+        super.onEvent(event);
+    }
+
+    private WizardMgtPanel<T> addNewItemPanelBuilder(final AjaxWizardBuilder<T> panelBuilder) {
+        this.newItemPanelBuilder = panelBuilder;
+
+        if (this.newItemPanelBuilder != null) {
+            addAjaxLink.setEnabled(true);
+            addAjaxLink.setVisible(true);
+        }
+
+        return this;
+    }
+
+    private WizardMgtPanel<T> addNotificationPanel(final NotificationPanel notificationPanel) {
+        this.notificationPanel = notificationPanel;
+        return this;
+    }
+
+    /**
+     * PanelInWizard abstract builder.
+     *
+     * @param <T> list item reference type.
+     */
+    public abstract static class Builder<T extends Serializable> implements Serializable {
+
+        private static final long serialVersionUID = 1L;
+
+        protected final PageReference pageRef;
+
+        protected final Class<T> reference;
+
+        private AjaxWizardBuilder<T> newItemPanelBuilder;
+
+        private NotificationPanel notificationPanel;
+
+        protected Builder(final Class<T> reference, final PageReference pageRef) {
+            this.pageRef = pageRef;
+            this.reference = reference;
+        }
+
+        protected abstract WizardMgtPanel<T> newInstance(final String id);
+
+        /**
+         * Builds a list view.
+         *
+         * @param id component id.
+         * @return List view.
+         */
+        public WizardMgtPanel<T> build(final String id) {
+            return newInstance(id).addNewItemPanelBuilder(newItemPanelBuilder).addNotificationPanel(notificationPanel);
+        }
+
+        public Builder<T> addNewItemPanelBuilder(final AjaxWizardBuilder<T> panelBuilder) {
+            this.newItemPanelBuilder = panelBuilder;
+            return this;
+        }
+
+        public Builder<T> addNotificationPanel(final NotificationPanel notificationPanel) {
+            this.notificationPanel = notificationPanel;
+            return this;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
new file mode 100644
index 0000000..62707cc
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyObjectWizardBuilder.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed 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.wizards.any;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.ResourceModel;
+import org.apache.wicket.model.StringResourceModel;
+
+public class AnyObjectWizardBuilder extends AjaxWizardBuilder<AnyTO> implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    private final AnyTO anyTO;
+
+    private final LoadableDetachableModel<List<String>> anyTypes = new LoadableDetachableModel<List<String>>() {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected List<String> load() {
+            final List<String> currentlyAdded = new ArrayList<>();
+            return currentlyAdded;
+        }
+    };
+
+    /**
+     * The object type specification step.
+     */
+    private final class ObjectType extends WizardStep {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Construct.
+         */
+        ObjectType(final AnyTO item) {
+            super(new ResourceModel("type.title", StringUtils.EMPTY),
+                    new ResourceModel("type.summary", StringUtils.EMPTY), new Model<AnyTO>(item));
+
+            add(new AjaxDropDownChoicePanel<String>("type", "type", new PropertyModel<String>(item, "anyType"), false).
+                    setChoices(anyTypes).
+                    setStyleSheet("form-control").
+                    setRequired(true));
+
+            add(new TextField<String>(
+                    "class", new PropertyModel<String>(item, "objectClass")).setRequired(true));
+        }
+    }
+
+    /**
+     * Mapping definition step.
+     */
+    private final class Mapping extends WizardStep {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Construct.
+         */
+        Mapping(final AnyTO item) {
+            setTitleModel(new ResourceModel("mapping.title", "Mapping"));
+            setSummaryModel(new StringResourceModel("mapping.summary", this, new Model<AnyTO>(item)));
+        }
+    }
+
+    /**
+     * AccountLink specification step.
+     */
+    private final class ConnObjectLink extends WizardStep {
+
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Construct.
+         */
+        ConnObjectLink(final AnyTO item) {
+            super(new ResourceModel("link.title", StringUtils.EMPTY),
+                    new ResourceModel("link.summary", StringUtils.EMPTY));
+
+            final WebMarkupContainer connObjectLinkContainer = new WebMarkupContainer("connObjectLinkContainer");
+            connObjectLinkContainer.setOutputMarkupId(true);
+            add(connObjectLinkContainer);
+        }
+    }
+
+    /**
+     * Construct.
+     *
+     * @param id The component id
+     * @param anyTO external resource to be updated.
+     * @param pageRef Caller page reference.
+     */
+    public AnyObjectWizardBuilder(final String id, final AnyTO anyTO, final PageReference pageRef) {
+        super(id, new AnyObjectTO(), pageRef);
+        this.anyTO = anyTO;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final AnyTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new ObjectType(modelObject));
+        wizardModel.add(new Mapping(modelObject));
+        wizardModel.add(new ConnObjectLink(modelObject));
+        return wizardModel;
+    }
+
+    @Override
+    protected void onCancelInternal(final AnyTO modelObject) {
+        // d nothing
+    }
+
+    @Override
+    protected void onApplyInternal(final AnyTO modelObject) {
+        // do nothing
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
new file mode 100644
index 0000000..c46f135
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed 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.wizards.any;
+
+import java.io.Serializable;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Mode;
+import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.wizards.AjaxWizardBuilder;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+
+public class AnyWizardBuilder<T extends AnyTO> extends AjaxWizardBuilder<T> implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    protected final AnyTypeRestClient anyTypeRestClient = new AnyTypeRestClient();
+
+    protected final List<String> anyTypeClasses;
+
+    /**
+     * Construct.
+     *
+     * @param id The component id
+     * @param anyTO any
+     * @param anyTypeClasses
+     * @param pageRef Caller page reference.
+     */
+    public AnyWizardBuilder(
+            final String id, final T anyTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+        super(id, anyTO, pageRef);
+        this.anyTypeClasses = anyTypeClasses;
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final T modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new PlainAttrs(modelObject, null, Mode.ADMIN, anyTypeClasses.toArray(new String[] {})));
+        wizardModel.add(new DerAttrs(modelObject, anyTypeClasses.toArray(new String[] {})));
+        wizardModel.add(new VirAttrs(modelObject, anyTypeClasses.toArray(new String[] {})));
+        return wizardModel;
+    }
+
+    @Override
+    protected void onCancelInternal(final T modelObject) {
+        // do nothing
+    }
+
+    @Override
+    protected void onApplyInternal(final T modelObject) {
+        if (!(modelObject instanceof AnyObjectTO)) {
+            throw new IllegalArgumentException();
+        }
+
+        final ProvisioningResult<AnyObjectTO> actual;
+
+        if (modelObject.getKey() == 0) {
+            actual = anyTypeRestClient.create(AnyObjectTO.class.cast(modelObject));
+        } else {
+            final AnyObjectPatch patch = AnyOperations.diff(modelObject, getDefaultItem(), true);
+
+            // update user just if it is changed
+            if (!patch.isEmpty()) {
+                actual = anyTypeRestClient.update(getDefaultItem().getETagValue(), patch);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
new file mode 100644
index 0000000..42af808
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/DerAttrs.java
@@ -0,0 +1,137 @@
+/*
+ * 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.wizards.any;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.DerSchemaTO;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.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;
+
+public class DerAttrs extends WizardStep {
+
+    private static final long serialVersionUID = -5387344116983102292L;
+
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
+
+    public <T extends AnyTO> DerAttrs(final T entityTO, final String... anyTypeClass) {
+
+        setOutputMarkupId(true);
+
+        final IModel<List<String>> derSchemas = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                List<DerSchemaTO> derSchemaNames = schemaRestClient.getSchemas(SchemaType.DERIVED, anyTypeClass);
+
+                return new ArrayList<>(CollectionUtils.collect(derSchemaNames, new Transformer<DerSchemaTO, String>() {
+
+                    @Override
+                    public String transform(final DerSchemaTO input) {
+                        return input.getKey();
+                    }
+                }));
+            }
+        };
+
+        final Map<String, AttrTO> derAttrMap = entityTO.getDerAttrMap();
+        CollectionUtils.collect(derSchemas.getObject(), new Transformer<String, AttrTO>() {
+
+            @Override
+            public AttrTO transform(final String input) {
+                AttrTO attrTO = derAttrMap.get(input);
+                if (attrTO == null) {
+                    attrTO = new AttrTO();
+                    attrTO.setSchema(input);
+                }
+                return attrTO;
+            }
+        }, entityTO.getDerAttrs());
+
+        final Fragment fragment;
+        if (entityTO.getDerAttrs().isEmpty()) {
+            // show empty list message
+            fragment = new Fragment("content", "empty", this);
+        } else {
+            fragment = new Fragment("content", "attributes", this);
+
+            final WebMarkupContainer attributesContainer = new WebMarkupContainer("derAttrContainer");
+            attributesContainer.setOutputMarkupId(true);
+            fragment.add(attributesContainer);
+
+            ListView<AttrTO> attributes = new ListView<AttrTO>("attrs",
+                    new PropertyModel<List<AttrTO>>(entityTO, "derAttrs") {
+
+                        private static final long serialVersionUID = 1L;
+
+                        @Override
+                        public List<AttrTO> getObject() {
+                            return new ArrayList<>(entityTO.getDerAttrs());
+                        }
+
+                    }) {
+
+                        private static final long serialVersionUID = 9101744072914090143L;
+
+                        @Override
+                        protected void populateItem(final ListItem<AttrTO> item) {
+                            final AttrTO attrTO = item.getModelObject();
+
+                            final IModel<String> model;
+                            final List<String> values = attrTO.getValues();
+                            if (values == null || values.isEmpty()) {
+                                model = new ResourceModel("derived.emptyvalue.message", StringUtils.EMPTY);
+                            } else {
+                                model = new Model<String>(values.get(0));
+                            }
+
+                            final AjaxTextFieldPanel panel = new AjaxTextFieldPanel("panel", attrTO.getSchema(), model);
+
+                            panel.setEnabled(false);
+                            panel.setRequired(true);
+                            panel.setOutputMarkupId(true);
+                            item.add(panel);
+
+                        }
+                    };
+            attributesContainer.add(attributes);
+        }
+
+        add(fragment);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
new file mode 100644
index 0000000..63ff592
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupDetails.java
@@ -0,0 +1,293 @@
+/*
+ * 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.wizards.any;
+
+import org.apache.syncope.client.console.commons.JexlHelpUtils;
+import org.apache.syncope.client.console.rest.GroupRestClient;
+import org.apache.syncope.client.console.rest.UserRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.Page;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
+import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GroupDetails extends WizardStep {
+
+    private static final long serialVersionUID = 855618618337931784L;
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(GroupDetails.class);
+
+    private final UserRestClient userRestClient = new UserRestClient();
+
+    private final GroupRestClient groupRestClient = new GroupRestClient();
+
+    private final WebMarkupContainer ownerContainer;
+
+    private final OwnerModel userOwnerModel;
+
+    private final OwnerModel groupOwnerModel;
+
+    public GroupDetails(final GroupTO groupTO, final boolean templateMode) {
+        ownerContainer = new WebMarkupContainer("ownerContainer");
+        ownerContainer.setOutputMarkupId(true);
+        this.add(ownerContainer);
+
+        final ModalWindow userOwnerSelectWin = new ModalWindow("userOwnerSelectWin");
+        userOwnerSelectWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        userOwnerSelectWin.setCookieName("create-userOwnerSelect-modal");
+        this.add(userOwnerSelectWin);
+        final ModalWindow groupOwnerSelectWin = new ModalWindow("groupOwnerSelectWin");
+        groupOwnerSelectWin.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
+        groupOwnerSelectWin.setCookieName("create-groupOwnerSelect-modal");
+        this.add(groupOwnerSelectWin);
+
+        final AjaxTextFieldPanel name
+                = new AjaxTextFieldPanel("name", "name", new PropertyModel<String>(groupTO, "name"));
+
+        final WebMarkupContainer jexlHelp = JexlHelpUtils.getJexlHelpWebContainer("jexlHelp");
+
+        final AjaxLink<Void> questionMarkJexlHelp = JexlHelpUtils.getAjaxLink(jexlHelp, "questionMarkJexlHelp");
+        this.add(questionMarkJexlHelp);
+        questionMarkJexlHelp.add(jexlHelp);
+
+        if (!templateMode) {
+            name.addRequiredLabel();
+            questionMarkJexlHelp.setVisible(false);
+        }
+        this.add(name);
+
+        userOwnerModel = new OwnerModel(groupTO, AnyTypeKind.USER);
+        @SuppressWarnings("unchecked")
+        final AjaxTextFieldPanel userOwner = new AjaxTextFieldPanel("userOwner", "userOwner", userOwnerModel);
+        userOwner.setReadOnly(true);
+        userOwner.setOutputMarkupId(true);
+        ownerContainer.add(userOwner);
+        final AjaxLink<Void> userOwnerSelect = new IndicatingAjaxLink<Void>("userOwnerSelect") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                userOwnerSelectWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+//                        return new UserOwnerSelectModalPage(getPage().getPageReference(), userOwnerSelectWin);
+                        return null;
+                    }
+                });
+                userOwnerSelectWin.show(target);
+            }
+        };
+        ownerContainer.add(userOwnerSelect.setEnabled(false));
+        final IndicatingAjaxLink<Void> userOwnerReset = new IndicatingAjaxLink<Void>("userOwnerReset") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                userOwnerModel.setObject(null);
+                target.add(userOwner);
+            }
+        };
+        ownerContainer.add(userOwnerReset.setEnabled(false));
+
+        groupOwnerModel = new OwnerModel(groupTO, AnyTypeKind.GROUP);
+        @SuppressWarnings("unchecked")
+        final AjaxTextFieldPanel groupOwner = new AjaxTextFieldPanel("groupOwner", "groupOwner", groupOwnerModel);
+        groupOwner.setReadOnly(true);
+        groupOwner.setOutputMarkupId(true);
+        ownerContainer.add(groupOwner);
+        final AjaxLink<Void> groupOwnerSelect = new IndicatingAjaxLink<Void>("groupOwnerSelect") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                userOwnerSelectWin.setPageCreator(new ModalWindow.PageCreator() {
+
+                    private static final long serialVersionUID = -7834632442532690940L;
+
+                    @Override
+                    public Page createPage() {
+//                        return new GroupSelectModalPage(getPage().getPageReference(), userOwnerSelectWin,
+//                                GroupOwnerSelectPayload.class);
+                        return null;
+                    }
+                });
+                userOwnerSelectWin.show(target);
+            }
+        };
+        ownerContainer.add(groupOwnerSelect.setEnabled(false));
+        final IndicatingAjaxLink<Void> groupOwnerReset = new IndicatingAjaxLink<Void>("groupOwnerReset") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                groupOwnerModel.setObject(null);
+                target.add(groupOwner);
+            }
+        };
+        ownerContainer.add(groupOwnerReset.setEnabled(false));
+    }
+
+    /**
+     * This is waiting for events from opened modal windows: first to get the selected user / group, then to update the
+     * respective text panel.
+     *
+     * {@inheritDoc }
+     *
+     * @param event
+     */
+    @Override
+    public void onEvent(final IEvent<?> event) {
+        super.onEvent(event);
+
+        if (event.getPayload() instanceof UserOwnerSelectPayload) {
+            userOwnerModel.setObject(((UserOwnerSelectPayload) event.getPayload()).getUserId());
+        }
+        if (event.getPayload() instanceof GroupOwnerSelectPayload) {
+            groupOwnerModel.setObject(((GroupOwnerSelectPayload) event.getPayload()).getGroupId());
+        }
+
+        if (event.getPayload() instanceof AjaxRequestTarget) {
+            ((AjaxRequestTarget) event.getPayload()).add(ownerContainer);
+        }
+    }
+
+    private class OwnerModel implements IModel {
+
+        private static final long serialVersionUID = -3865621970810102714L;
+
+        private final GroupTO groupTO;
+
+        private final AnyTypeKind type;
+
+        OwnerModel(final GroupTO groupTO, final AnyTypeKind type) {
+            this.groupTO = groupTO;
+            this.type = type;
+        }
+
+        @Override
+        public Object getObject() {
+            String object = null;
+
+            switch (type) {
+                case USER:
+                    if (groupTO.getUserOwner() != null) {
+                        UserTO user = null;
+                        try {
+                            user = userRestClient.read(groupTO.getUserOwner());
+                        } catch (Exception e) {
+                            LOG.warn("Could not find user with id {}, ignoring", groupTO.getUserOwner(), e);
+                        }
+                        if (user == null) {
+                            groupTO.setUserOwner(null);
+                        } else {
+                            object = user.getKey() + " " + user.getUsername();
+                        }
+                    }
+                    break;
+
+                case GROUP:
+                    GroupTO group = null;
+                    if (groupTO.getGroupOwner() != null) {
+                        try {
+                            group = groupRestClient.read(groupTO.getGroupOwner());
+                        } catch (Exception e) {
+                            LOG.warn("Could not find group with id {}, ignoring", groupTO.getGroupOwner(), e);
+                        }
+                        if (group == null) {
+                            groupTO.setGroupOwner(null);
+                        } else {
+                            object = group.getDisplayName();
+                        }
+                    }
+                    break;
+
+                default:
+            }
+
+            return object;
+        }
+
+        @Override
+        public void setObject(final Object object) {
+            switch (type) {
+                case USER:
+                    groupTO.setUserOwner((Long) object);
+                    break;
+
+                case GROUP:
+                    groupTO.setGroupOwner((Long) object);
+                    break;
+
+                default:
+            }
+        }
+
+        @Override
+        public void detach() {
+            // ignore
+        }
+    }
+
+    public static class UserOwnerSelectPayload {
+
+        private final Long userId;
+
+        public UserOwnerSelectPayload(final Long userId) {
+            this.userId = userId;
+        }
+
+        public Long getUserId() {
+            return userId;
+        }
+    }
+
+    public static class GroupOwnerSelectPayload {
+
+        private final Long groupId;
+
+        public GroupOwnerSelectPayload(final Long groupId) {
+            this.groupId = groupId;
+        }
+
+        public Long getGroupId() {
+            return groupId;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
new file mode 100644
index 0000000..9ae9332
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/GroupWizardBuilder.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed 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.wizards.any;
+
+import java.util.List;
+import org.apache.syncope.client.console.rest.GroupRestClient;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+
+public class GroupWizardBuilder extends AnyWizardBuilder<GroupTO> {
+
+    private static final long serialVersionUID = 1L;
+
+    private final GroupRestClient groupRestClient = new GroupRestClient();
+
+    /**
+     * Construct.
+     *
+     * @param id The component id
+     * @param groupTO any
+     * @param anyTypeClasses
+     * @param pageRef Caller page reference.
+     */
+    public GroupWizardBuilder(
+            final String id, final GroupTO groupTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+        super(id, groupTO, anyTypeClasses, pageRef);
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final GroupTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new GroupDetails(modelObject, false));
+        return super.buildModelSteps(modelObject, wizardModel);
+    }
+
+    @Override
+    protected void onApplyInternal(final GroupTO modelObject) {
+        final ProvisioningResult<GroupTO> actual;
+
+        if (modelObject.getKey() == 0) {
+            actual = groupRestClient.create(modelObject);
+        } else {
+            final GroupPatch patch = AnyOperations.diff(modelObject, getDefaultItem(), true);
+
+            // update user just if it is changed
+            if (!patch.isEmpty()) {
+                actual = groupRestClient.update(getDefaultItem().getETagValue(), patch);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
new file mode 100644
index 0000000..4501856
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/PlainAttrs.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.client.console.wizards.any;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+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.JexlHelpUtils;
+import org.apache.syncope.client.console.commons.Mode;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.FieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.client.console.wicket.markup.html.form.SpinnerFieldPanel;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+
+public class PlainAttrs extends WizardStep {
+
+    private static final long serialVersionUID = 552437609667518888L;
+
+    private final SchemaRestClient schemaRestClient = new SchemaRestClient();
+
+    private final AnyTO entityTO;
+
+    private final Mode mode;
+
+    private Map<String, PlainSchemaTO> schemas = new LinkedHashMap<>();
+
+    private final String[] anyTypeClass;
+
+    public <T extends AnyTO> PlainAttrs(final T entityTO, final Form<?> form, final Mode mode,
+            final String... anyTypeClass) {
+        this.setOutputMarkupId(true);
+
+        this.entityTO = entityTO;
+        this.mode = mode;
+        this.anyTypeClass = anyTypeClass;
+
+        setSchemas();
+        setAttrs();
+
+        add(new ListView<AttrTO>("schemas", new PropertyModel<List<AttrTO>>(entityTO, "plainAttrs") {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public List<AttrTO> getObject() {
+                return new ArrayList<>(super.getObject());
+            }
+
+        }) {
+
+            private static final long serialVersionUID = 9101744072914090143L;
+
+            @Override
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            protected void populateItem(final ListItem<AttrTO> item) {
+                final AttrTO attributeTO = (AttrTO) item.getDefaultModelObject();
+
+                final WebMarkupContainer jexlHelp = JexlHelpUtils.getJexlHelpWebContainer("jexlHelp");
+
+                final AjaxLink<Void> questionMarkJexlHelp = JexlHelpUtils.getAjaxLink(jexlHelp, "questionMarkJexlHelp");
+                item.add(questionMarkJexlHelp);
+                questionMarkJexlHelp.add(jexlHelp);
+
+                if (mode != Mode.TEMPLATE) {
+                    questionMarkJexlHelp.setVisible(false);
+                }
+
+                final FieldPanel panel = getFieldPanel(schemas.get(attributeTO.getSchema()), form, attributeTO);
+
+                if (mode == Mode.TEMPLATE || !schemas.get(attributeTO.getSchema()).isMultivalue()) {
+                    item.add(panel);
+                } else {
+                    item.add(new MultiFieldPanel<String>(
+                            "panel", attributeTO.getSchema(), new PropertyModel<List<String>>(attributeTO, "values"),
+                            panel));
+                }
+            }
+        }
+        );
+    }
+
+    private void setSchemas() {
+
+        AttrTO attrLayout = null;
+        final List<PlainSchemaTO> schemaTOs = schemaRestClient.getSchemas(SchemaType.PLAIN, anyTypeClass);
+
+        schemas.clear();
+
+        if (attrLayout != null && mode != Mode.TEMPLATE) {
+            // 1. remove attributes not selected for display
+            schemaRestClient.filter(schemaTOs, attrLayout.getValues(), true);
+            // 2. sort remainig attributes according to configuration, e.g. attrLayout
+            final Map<String, Integer> attrLayoutMap = new HashMap<>(attrLayout.getValues().size());
+            for (int i = 0; i < attrLayout.getValues().size(); i++) {
+                attrLayoutMap.put(attrLayout.getValues().get(i), i);
+            }
+            Collections.sort(schemaTOs, new Comparator<PlainSchemaTO>() {
+
+                @Override
+                public int compare(final PlainSchemaTO schema1, final PlainSchemaTO schema2) {
+                    int value = 0;
+
+                    if (attrLayoutMap.get(schema1.getKey()) > attrLayoutMap.get(schema2.getKey())) {
+                        value = 1;
+                    } else if (attrLayoutMap.get(schema1.getKey()) < attrLayoutMap.get(schema2.getKey())) {
+                        value = -1;
+                    }
+
+                    return value;
+                }
+            });
+        }
+        for (PlainSchemaTO schemaTO : schemaTOs) {
+            schemas.put(schemaTO.getKey(), schemaTO);
+        }
+    }
+
+    private void setAttrs() {
+        final List<AttrTO> entityData = new ArrayList<>();
+
+        final Map<String, AttrTO> attrMap = entityTO.getPlainAttrMap();
+
+        for (PlainSchemaTO schema : schemas.values()) {
+            final AttrTO attributeTO = new AttrTO();
+            attributeTO.setSchema(schema.getKey());
+
+            if (attrMap.get(schema.getKey()) == null || attrMap.get(schema.getKey()).getValues().isEmpty()) {
+                attributeTO.getValues().add("");
+
+                // is important to set readonly only after values setting
+                attributeTO.setReadonly(schema.isReadonly());
+            } else {
+                attributeTO.getValues().addAll(attrMap.get(schema.getKey()).getValues());
+            }
+            entityData.add(attributeTO);
+        }
+
+        entityTO.getPlainAttrs().clear();
+        entityTO.getPlainAttrs().addAll(entityData);
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private FieldPanel getFieldPanel(final PlainSchemaTO schemaTO, final Form form, final AttrTO attributeTO) {
+        final boolean required = mode == Mode.TEMPLATE
+                ? false
+                : schemaTO.getMandatoryCondition().equalsIgnoreCase("true");
+
+        final boolean readOnly = mode == Mode.TEMPLATE ? false : schemaTO.isReadonly();
+
+        final AttrSchemaType type = mode == Mode.TEMPLATE ? AttrSchemaType.String : schemaTO.getType();
+
+        final FieldPanel panel;
+        switch (type) {
+            case Boolean:
+                panel = new AjaxCheckBoxPanel("panel", schemaTO.getKey(), new Model<Boolean>(), false);
+                panel.setRequired(required);
+                break;
+
+//            case Date:
+//                final String dataPattern = schemaTO.getConversionPattern() == null
+//                        ? SyncopeConstants.DEFAULT_DATE_PATTERN
+//                        : schemaTO.getConversionPattern();
+//
+//                if (dataPattern.contains("H")) {
+//                    panel = new DateTimeFieldPanel("panel", schemaTO.getKey(), new Model<Date>(), dataPattern);
+//
+//                    if (required) {
+//                        panel.addRequiredLabel();
+//                        ((DateTimeFieldPanel) panel).setFormValidator(form);
+//                    }
+//                    panel.setStyleSheet("ui-widget-content ui-corner-all");
+//                } else {
+//                    panel = new DateTextFieldPanel("panel", schemaTO.getKey(), new Model<Date>(), dataPattern);
+//
+//                    if (required) {
+//                        panel.addRequiredLabel();
+//                    }
+//                }
+//                break;
+            case Enum:
+                panel = new AjaxDropDownChoicePanel<String>("panel", schemaTO.getKey(), new Model<String>());
+                ((AjaxDropDownChoicePanel<String>) panel).setChoices(getEnumeratedValues(schemaTO));
+
+                if (StringUtils.isNotBlank(schemaTO.getEnumerationKeys())) {
+                    ((AjaxDropDownChoicePanel) panel).setChoiceRenderer(new IChoiceRenderer<String>() {
+
+                        private static final long serialVersionUID = -3724971416312135885L;
+
+                        private final Map<String, String> valueMap = getEnumeratedKeyValues(schemaTO);
+
+                        @Override
+                        public String getDisplayValue(final String value) {
+                            return valueMap.get(value) == null ? value : valueMap.get(value);
+                        }
+
+                        @Override
+                        public String getIdValue(final String value, final int i) {
+                            return value;
+                        }
+
+                        @Override
+                        public String getObject(
+                                final String id, final IModel<? extends List<? extends String>> choices) {
+                            return id;
+                        }
+                    });
+                }
+
+                if (required) {
+                    panel.addRequiredLabel();
+                }
+                break;
+
+            case Long:
+                panel = new SpinnerFieldPanel<Long>("panel", schemaTO.getKey(), Long.class, new Model<Long>());
+
+                if (required) {
+                    panel.addRequiredLabel();
+                }
+                break;
+
+            case Double:
+                panel = new SpinnerFieldPanel<Double>("panel", schemaTO.getKey(), Double.class, new Model<Double>());
+
+                if (required) {
+                    panel.addRequiredLabel();
+                }
+                break;
+
+            case Binary:
+                panel = new BinaryFieldPanel("panel", schemaTO.getKey(), new Model<String>(),
+                        schemas.containsKey(schemaTO.getKey())
+                                ? schemas.get(schemaTO.getKey()).getMimeType()
+                                : null);
+
+                if (required) {
+                    panel.addRequiredLabel();
+                }
+                break;
+
+            default:
+                panel = new AjaxTextFieldPanel("panel", schemaTO.getKey(), new Model<String>(), false);
+
+                if (required) {
+                    panel.addRequiredLabel();
+                }
+        }
+
+        panel.setReadOnly(readOnly);
+        panel.setNewModel(attributeTO.getValues());
+
+        return panel;
+    }
+
+    private Map<String, String> getEnumeratedKeyValues(final PlainSchemaTO schemaTO) {
+        final Map<String, String> res = new HashMap<>();
+
+        final String[] values = StringUtils.isBlank(schemaTO.getEnumerationValues())
+                ? new String[0]
+                : schemaTO.getEnumerationValues().split(SyncopeConstants.ENUM_VALUES_SEPARATOR);
+
+        final String[] keys = StringUtils.isBlank(schemaTO.getEnumerationKeys())
+                ? new String[0]
+                : schemaTO.getEnumerationKeys().split(SyncopeConstants.ENUM_VALUES_SEPARATOR);
+
+        for (int i = 0; i < values.length; i++) {
+            res.put(values[i].trim(), keys.length > i ? keys[i].trim() : null);
+        }
+
+        return res;
+    }
+
+    private List<String> getEnumeratedValues(final PlainSchemaTO schemaTO) {
+        final List<String> res = new ArrayList<>();
+
+        final String[] values = StringUtils.isBlank(schemaTO.getEnumerationValues())
+                ? new String[0]
+                : schemaTO.getEnumerationValues().split(SyncopeConstants.ENUM_VALUES_SEPARATOR);
+
+        for (String value : values) {
+            res.add(value.trim());
+        }
+
+        return res;
+    }
+
+//    @Override
+//    public void onEvent(final IEvent<?> event) {
+//        if ((event.getPayload() instanceof GroupAttrTemplatesChange)) {
+//            final GroupAttrTemplatesChange update = (GroupAttrTemplatesChange) event.getPayload();
+//            if (attrTemplates != null && update.getType() == AttrTemplatesPanel.Type.gPlainAttrTemplates) {
+//                setSchemas();
+//                setAttrs();
+//                update.getTarget().add(this);
+//            }
+//        }
+//    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
new file mode 100644
index 0000000..5918fd0
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserDetails.java
@@ -0,0 +1,104 @@
+/*
+ * 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.wizards.any;
+
+import org.apache.syncope.client.console.commons.JexlHelpUtils;
+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.ajax.markup.html.AjaxLink;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+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.model.Model;
+import org.apache.wicket.model.PropertyModel;
+
+public class UserDetails extends WizardStep {
+
+    private static final long serialVersionUID = 6592027822510220463L;
+
+    public UserDetails(final UserTO userTO, final boolean resetPassword, final boolean templateMode) {
+        // ------------------------
+        // Username
+        // ------------------------
+        final FieldPanel<String> username = new AjaxTextFieldPanel("username", "username",
+                new PropertyModel<String>(userTO, "username"));
+
+        final WebMarkupContainer jexlHelp = JexlHelpUtils.getJexlHelpWebContainer("usernameJexlHelp");
+
+        final AjaxLink<?> questionMarkJexlHelp = JexlHelpUtils.getAjaxLink(jexlHelp, "usernameQuestionMarkJexlHelp");
+        add(questionMarkJexlHelp);
+        questionMarkJexlHelp.add(jexlHelp);
+
+        if (!templateMode) {
+            username.addRequiredLabel();
+            questionMarkJexlHelp.setVisible(false);
+        }
+        add(username);
+        // ------------------------
+
+        // ------------------------
+        // Password
+        // ------------------------
+        final Form<?> form = new Form<>("passwordInnerForm");
+        add(form);
+        
+        final WebMarkupContainer pwdJexlHelp = JexlHelpUtils.getJexlHelpWebContainer("pwdJexlHelp");
+
+        final AjaxLink<?> pwdQuestionMarkJexlHelp = JexlHelpUtils.getAjaxLink(pwdJexlHelp, "pwdQuestionMarkJexlHelp");
+        form.add(pwdQuestionMarkJexlHelp);
+        pwdQuestionMarkJexlHelp.add(pwdJexlHelp);
+
+        FieldPanel<String> passwordField
+                = new AjaxPasswordFieldPanel("password", "password", new PropertyModel<String>(userTO, "password"));
+        passwordField.setRequired(true);
+        passwordField.setMarkupId("password");
+        passwordField.setPlaceholder("password");
+        ((PasswordTextField) passwordField.getField()).setResetPassword(true);
+        form.add(passwordField);
+
+        FieldPanel<String> confirmPasswordField
+                = new AjaxPasswordFieldPanel("confirmPassword", "confirmPassword", new Model<String>());
+        confirmPasswordField.setRequired(true);
+        confirmPasswordField.setMarkupId("confirmPassword");
+        confirmPasswordField.setPlaceholder("confirmPassword");
+        ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(true);
+        form.add(confirmPasswordField);
+
+        form.add(new EqualPasswordInputValidator(passwordField.getField(), confirmPasswordField.getField()));
+
+        if (templateMode) {
+            confirmPasswordField.setEnabled(false);
+            confirmPasswordField.setVisible(false);
+        } else {
+            pwdQuestionMarkJexlHelp.setVisible(false);
+
+            ((PasswordTextField) passwordField.getField()).setResetPassword(resetPassword);
+
+            if (!resetPassword) {
+                confirmPasswordField.getField().setModelObject(userTO.getPassword());
+            }
+            ((PasswordTextField) confirmPasswordField.getField()).setResetPassword(resetPassword);
+        }
+        // ------------------------
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
new file mode 100644
index 0000000..21d990f
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/UserWizardBuilder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 The Apache Software Foundation.
+ *
+ * Licensed 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.wizards.any;
+
+import java.util.List;
+import org.apache.syncope.client.console.rest.UserRestClient;
+import org.apache.syncope.common.lib.AnyOperations;
+import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.model.Model;
+
+public class UserWizardBuilder extends AnyWizardBuilder<UserTO> {
+
+    private static final long serialVersionUID = 1L;
+
+    private final UserRestClient userRestClient = new UserRestClient();
+
+    /**
+     * Construct.
+     *
+     * @param id The component id
+     * @param userTO any
+     * @param anyTypeClasses
+     * @param pageRef Caller page reference.
+     */
+    public UserWizardBuilder(
+            final String id, final UserTO userTO, final List<String> anyTypeClasses, final PageReference pageRef) {
+        super(id, userTO, anyTypeClasses, pageRef);
+    }
+
+    @Override
+    protected WizardModel buildModelSteps(final UserTO modelObject, final WizardModel wizardModel) {
+        wizardModel.add(new UserDetails(modelObject, false, false));
+        return super.buildModelSteps(modelObject, wizardModel);
+    }
+
+    @Override
+    protected void onApplyInternal(final UserTO modelObject) {
+        Model<Boolean> storePassword = new Model<>(true);
+
+        final ProvisioningResult<UserTO> actual;
+
+        if (modelObject.getKey() == 0) {
+            actual = userRestClient.create(modelObject, storePassword.getObject());
+        } else {
+            final UserPatch patch = AnyOperations.diff(modelObject, getDefaultItem(), true);
+
+//            if (statusPanel != null) {
+//                patch.setPwdPropRequest(statusPanel.getStatusMod());
+//            }
+            // update user just if it is changed
+            if (!patch.isEmpty()) {
+                actual = userRestClient.update(getDefaultItem().getETagValue(), patch);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
new file mode 100644
index 0000000..3bfcc17
--- /dev/null
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/VirAttrs.java
@@ -0,0 +1,141 @@
+/*
+ * 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.wizards.any;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.rest.SchemaRestClient;
+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.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.LoadableDetachableModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.PropertyModel;
+
+public class VirAttrs extends WizardStep {
+
+    private static final long serialVersionUID = -7982691107029848579L;
+
+    private SchemaRestClient schemaRestClient = new SchemaRestClient();
+
+    private final Map<String, VirSchemaTO> schemas = new TreeMap<String, VirSchemaTO>();
+
+    public <T extends AnyTO> VirAttrs(final T entityTO, final String... anyTypeClass) {
+        this.setOutputMarkupId(true);
+
+        final IModel<List<String>> virSchemas = new LoadableDetachableModel<List<String>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                List<VirSchemaTO> schemaTOs = schemaRestClient.getSchemas(SchemaType.VIRTUAL, anyTypeClass);
+
+                schemas.clear();
+
+                for (VirSchemaTO schemaTO : schemaTOs) {
+                    schemas.put(schemaTO.getKey(), schemaTO);
+                }
+
+                return new ArrayList<>(schemas.keySet());
+            }
+        };
+
+        final Map<String, AttrTO> virAttrMap = entityTO.getVirAttrMap();
+        CollectionUtils.collect(virSchemas.getObject(), new Transformer<String, AttrTO>() {
+
+            @Override
+            public AttrTO transform(final String input) {
+                AttrTO attrTO = virAttrMap.get(input);
+                if (attrTO == null) {
+                    attrTO = new AttrTO();
+                    attrTO.setSchema(input);
+                    attrTO.getValues().add(StringUtils.EMPTY);
+                } else if (attrTO.getValues().isEmpty()) {
+                    attrTO.getValues().add("");
+                }
+
+                return attrTO;
+            }
+        }, entityTO.getVirAttrs());
+
+        final Fragment fragment;
+        if (entityTO.getVirAttrs().isEmpty()) {
+            // show empty list message
+            fragment = new Fragment("content", "empty", this);
+        } else {
+            fragment = new Fragment("content", "attributes", this);
+
+            final WebMarkupContainer attributesContainer = new WebMarkupContainer("virAttrContainer");
+            attributesContainer.setOutputMarkupId(true);
+            fragment.add(attributesContainer);
+
+            ListView<AttrTO> attributes = new ListView<AttrTO>("attrs",
+                    new PropertyModel<List<AttrTO>>(entityTO, "virAttrs") {
+
+                        private static final long serialVersionUID = 1L;
+
+                        @Override
+                        public List<AttrTO> getObject() {
+                            return new ArrayList<>(entityTO.getVirAttrs());
+                        }
+
+                    }) {
+
+                        private static final long serialVersionUID = 9101744072914090143L;
+
+                        @Override
+                        @SuppressWarnings("unchecked")
+                        protected void populateItem(final ListItem<AttrTO> item) {
+                            AttrTO attrTO = item.getModelObject();
+                            final VirSchemaTO schema = schemas.get(attrTO.getSchema());
+
+                            attrTO.setReadonly(schema.isReadonly());
+
+                            final AjaxTextFieldPanel panel
+                            = new AjaxTextFieldPanel("panel", attrTO.getSchema(), new Model<String>());
+
+                            item.add(new MultiFieldPanel<String>(
+                                            "panel",
+                                            schema.getKey(),
+                                            new PropertyModel<List<String>>(attrTO, "values"),
+                                            panel).setEnabled(!schema.isReadonly()));
+                        }
+                    };
+
+            attributesContainer.add(attributes);
+        }
+
+        add(fragment);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
index 1172035..8c20920 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/provision/ProvisionWizardBuilder.java
@@ -107,8 +107,7 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ProvisionTO> imple
                     setStyleSheet("form-control").
                     setRequired(true));
 
-            add(new TextField<String>(
-                    "class", new PropertyModel<String>(item, "objectClass")).setRequired(true));
+            add(new TextField<String>("class", new PropertyModel<String>(item, "objectClass")).setRequired(true));
         }
     }
 
@@ -211,12 +210,12 @@ public class ProvisionWizardBuilder extends AjaxWizardBuilder<ProvisionTO> imple
     }
 
     @Override
-    protected void onCancelInternal() {
-        // d nothing
+    protected void onCancelInternal(final ProvisionTO modelObject) {
+        // do nothing
     }
 
     @Override
-    protected void onApplyInternal() {
+    protected void onApplyInternal(final ProvisionTO modelObject) {
         // do nothing
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
index ad0f032..98bb267 100644
--- a/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
+++ b/client/console/src/main/resources/META-INF/resources/css/syncopeConsole.css
@@ -201,10 +201,25 @@ div.basepage-content{
   overflow: hidden;
 }
 
+.modal-body {
+  max-height: 600px;
+  overflow-y: auto;
+}
+
 .modal {
   background: rgba(0, 0, 0, 0) none repeat scroll 0 0;
 }
 
+.wizard-view {
+  max-height: 500px;
+  overflow: auto;
+  padding: 0px 15px 0px 5px;
+}
+
+.wizard-buttons {
+  padding: 10px 0px 5px 0px;
+}
+
 div.realms div.summarize {
   margin: 50px 100px;
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/047ac019/client/console/src/main/resources/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.html
----------------------------------------------------------------------
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.html
index 457ccc8..8a51c77 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/AbstractSearchResultPanel.html
@@ -21,8 +21,8 @@ under the License.
     <title>Search result panel</title>
   </head>
   <body>
-    <wicket:panel>
-      <div wicket:id="container">
+    <wicket:extend>
+      <div wicket:id="searchContainer">
         <span wicket:id="resultTable">[Table]</span>
 
         <span style="float:right">
@@ -38,10 +38,6 @@ under the License.
           </form>
         </span>
       </div>
-
-      <div wicket:id="modal">
-      </div>
-
-    </wicket:panel>
+    </wicket:extend>
   </body>
 </html>


Mime
View raw message