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:00:58 UTC

[27/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/wicket/markup/html/form/AjaxDecoratedCheckbox.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDecoratedCheckbox.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDecoratedCheckbox.java
new file mode 100644
index 0000000..547d91a
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDecoratedCheckbox.java
@@ -0,0 +1,55 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxEventBehavior;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
+import org.apache.wicket.model.IModel;
+
+/**
+ * AjaxCheckBox allowing AjaxCallDecorator.
+ */
+public abstract class AjaxDecoratedCheckbox extends AjaxCheckBox {
+
+    private static final long serialVersionUID = 7345848589265633002L;
+
+    public AjaxDecoratedCheckbox(final String id) {
+        this(id, null);
+    }
+
+    public AjaxDecoratedCheckbox(final String id, final IModel<Boolean> model) {
+        super(id, model);
+
+        add(new AjaxEventBehavior(Constants.ON_CLICK) {
+
+            private static final long serialVersionUID = -295188647830294610L;
+
+            @Override
+            protected void onEvent(final AjaxRequestTarget target) {
+                refreshComponent(target);
+            }
+        });
+    }
+
+    private void refreshComponent(final AjaxRequestTarget target) {
+        target.add(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDropDownChoicePanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDropDownChoicePanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDropDownChoicePanel.java
new file mode 100644
index 0000000..19e0e1d
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxDropDownChoicePanel.java
@@ -0,0 +1,87 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.ChoiceRenderer;
+import org.apache.wicket.markup.html.form.DropDownChoice;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class AjaxDropDownChoicePanel<T extends Serializable> extends FieldPanel<T> implements Cloneable {
+
+    private static final long serialVersionUID = -4716376580659196095L;
+
+    public AjaxDropDownChoicePanel(final String id, final String name, final IModel<T> model) {
+        this(id, name, model, true);
+    }
+
+    public AjaxDropDownChoicePanel(final String id, final String name, final IModel<T> model, boolean enableOnBlur) {
+        super(id, model);
+
+        field = new DropDownChoice<T>(
+                "dropDownChoiceField", model, Collections.<T>emptyList(), new ChoiceRenderer<T>());
+        add(field.setLabel(new Model<String>(name)).setOutputMarkupId(true));
+
+        if (enableOnBlur) {
+            field.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    // nothing to do
+                }
+            });
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public AjaxDropDownChoicePanel<T> setChoiceRenderer(final IChoiceRenderer renderer) {
+        ((DropDownChoice) field).setChoiceRenderer(renderer);
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public AjaxDropDownChoicePanel<T> setChoices(final List<T> choices) {
+        ((DropDownChoice) field).setChoices(choices);
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public AjaxDropDownChoicePanel<T> setChoices(final IModel<? extends List<? extends T>> choices) {
+        ((DropDownChoice) field).setChoices(choices);
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public FieldPanel clone() {
+        final AjaxDropDownChoicePanel<T> panel = (AjaxDropDownChoicePanel<T>) super.clone();
+        panel.setChoiceRenderer(((DropDownChoice) field).getChoiceRenderer());
+        panel.setChoices(((DropDownChoice) field).getChoices());
+        return panel;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPalettePanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPalettePanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPalettePanel.java
new file mode 100644
index 0000000..91b6306
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPalettePanel.java
@@ -0,0 +1,70 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.syncope.client.console.commons.SelectChoiceRenderer;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.util.ListModel;
+
+public class AjaxPalettePanel<T> extends AbstractFieldPanel<List<T>> {
+
+    private static final long serialVersionUID = 7738499668258805567L;
+
+    protected final Palette<T> palette;
+
+    public AjaxPalettePanel(final String id, final IModel<List<T>> model, final ListModel<T> choices) {
+        this(id, model, choices, false);
+    }
+
+    public AjaxPalettePanel(final String id, final IModel<List<T>> model, final ListModel<T> choices,
+            final boolean allowOrder) {
+
+        this(id, model, choices, new SelectChoiceRenderer<T>(), allowOrder, false);
+    }
+
+    public AjaxPalettePanel(final String id, final IModel<List<T>> model, final ListModel<T> choices,
+            final IChoiceRenderer<T> renderer, final boolean allowOrder, final boolean allowMoveAll) {
+
+        super(id, model);
+
+        this.palette = createPalette(model, choices, renderer, allowOrder, allowMoveAll);
+        add(palette.setOutputMarkupId(true));
+        setOutputMarkupId(true);
+    }
+
+    protected Palette<T> createPalette(final IModel<List<T>> model, final ListModel<T> choices,
+            final IChoiceRenderer<T> renderer, final boolean allowOrder, final boolean allowMoveAll) {
+
+        return new NonI18nPalette<T>("paletteField", model, choices, renderer, 8, allowOrder, allowMoveAll);
+    }
+
+    @Override
+    public AjaxPalettePanel<T> setModelObject(final List<T> object) {
+        palette.setDefaultModelObject(object);
+        return this;
+    }
+
+    public Collection<T> getModelCollection() {
+        return palette.getModelCollection();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPasswordFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPasswordFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPasswordFieldPanel.java
new file mode 100644
index 0000000..14fdf9e
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxPasswordFieldPanel.java
@@ -0,0 +1,60 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.form.PasswordTextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class AjaxPasswordFieldPanel extends FieldPanel<String> {
+
+    private static final long serialVersionUID = -5490115280336667460L;
+
+    public AjaxPasswordFieldPanel(final String id, final String name, final IModel<String> model) {
+        super(id, model);
+
+        field = new PasswordTextField("passwordField", model);
+        add(field.setLabel(new Model<String>(name)).setRequired(false).setOutputMarkupId(true));
+
+        if (!isReadOnly()) {
+            field.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget art) {
+                    // nothing to do
+                }
+            });
+        }
+    }
+
+    @Override
+    public FieldPanel<String> addRequiredLabel() {
+        if (!isRequired()) {
+            setRequired(true);
+        }
+
+        this.isRequiredLabelAdded = true;
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxTextFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxTextFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxTextFieldPanel.java
new file mode 100644
index 0000000..6854fb0
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/AjaxTextFieldPanel.java
@@ -0,0 +1,98 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
+import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteTextField;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.validation.IValidator;
+
+public class AjaxTextFieldPanel extends FieldPanel<String> implements Cloneable {
+
+    private static final long serialVersionUID = 238940918106696068L;
+
+    private List<String> choices = Collections.emptyList();
+
+    public AjaxTextFieldPanel(final String id, final String name, final IModel<String> model) {
+        super(id, model);
+
+        final AutoCompleteSettings settings = new AutoCompleteSettings();
+        settings.setShowCompleteListOnFocusGain(true);
+        settings.setShowListOnEmptyInput(true);
+
+        field = new AutoCompleteTextField<String>("textField", model, settings) {
+
+            private static final long serialVersionUID = -6648767303091874219L;
+
+            @Override
+            protected Iterator<String> getChoices(final String input) {
+                final Pattern pattern = Pattern.compile(".*" + Pattern.quote(input) + ".*", Pattern.CASE_INSENSITIVE);
+
+                final List<String> result = new ArrayList<String>();
+
+                for (String choice : choices) {
+                    if (pattern.matcher(choice).matches()) {
+                        result.add(choice);
+                    }
+                }
+
+                return result.iterator();
+            }
+        };
+        add(field.setLabel(new Model<String>(name)).setOutputMarkupId(true));
+
+        if (!isReadOnly()) {
+            field.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    // nothing to do
+                }
+            });
+        }
+    }
+
+    public void addValidator(final IValidator<? super String> validator) {
+        this.field.add(validator);
+    }
+
+    public void setChoices(final List<String> choices) {
+        if (choices != null) {
+            this.choices = choices;
+        }
+    }
+
+    @Override
+    public FieldPanel<String> clone() {
+        final AjaxTextFieldPanel panel = (AjaxTextFieldPanel) super.clone();
+        panel.setChoices(choices);
+        return panel;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
new file mode 100644
index 0000000..df91c7d
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
@@ -0,0 +1,211 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.io.ByteArrayInputStream;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.preview.PreviewUtil;
+import org.apache.syncope.client.console.commons.HttpResourceStream;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.syncope.client.console.pages.BaseModalPage;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.wicket.Component;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
+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.StatelessForm;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.form.upload.FileUpload;
+import org.apache.wicket.markup.html.form.upload.FileUploadField;
+import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.request.handler.resource.ResourceStreamRequestHandler;
+import org.apache.wicket.request.resource.ContentDisposition;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+import org.apache.wicket.util.crypt.Base64;
+import org.apache.wicket.util.lang.Bytes;
+
+public class BinaryFieldPanel extends FieldPanel<String> {
+
+    private static final long serialVersionUID = 6264462604183088931L;
+
+    private final String mimeType;
+
+    private final WebMarkupContainer container;
+
+    private final Link<Void> downloadLink;
+
+    private final Form<?> uploadForm;
+
+    private final Fragment emptyFragment;
+
+    @SpringBean
+    private PreviewUtil previewUtil;
+
+    public BinaryFieldPanel(final String id, final String name, final IModel<String> model, final String mimeType) {
+        super(id, model);
+        this.mimeType = mimeType;
+
+        uploadForm = new StatelessForm<Void>("uploadForm");
+        uploadForm.setMultiPart(true);
+        uploadForm.setMaxSize(Bytes.megabytes(4));
+        add(uploadForm);
+
+        container = new WebMarkupContainer("previewContainer");
+        container.setOutputMarkupId(true);
+
+        emptyFragment = new Fragment("panelPreview", "emptyFragment", container);
+        emptyFragment.setOutputMarkupId(true);
+        container.add(emptyFragment);
+        uploadForm.add(container);
+
+        field = new TextField<String>("textField", model);
+        add(field.setLabel(new Model<String>(name)).setOutputMarkupId(true));
+
+        uploadForm.add(new Label("preview", StringUtils.isBlank(mimeType) ? StringUtils.EMPTY : "(" + mimeType + ")"));
+
+        downloadLink = new Link<Void>("downloadLink") {
+
+            private static final long serialVersionUID = -4331619903296515985L;
+
+            @Override
+            public void onClick() {
+                try {
+                    HttpResourceStream stream = new HttpResourceStream(buildResponse());
+
+                    ResourceStreamRequestHandler rsrh = new ResourceStreamRequestHandler(stream);
+                    rsrh.setFileName(stream.getFilename() == null ? name : stream.getFilename());
+                    rsrh.setContentDisposition(ContentDisposition.ATTACHMENT);
+
+                    getRequestCycle().scheduleRequestHandlerAfterCurrent(rsrh);
+                } catch (Exception e) {
+                    error(getString(Constants.ERROR) + ": " + e.getMessage());
+                }
+            }
+        };
+        downloadLink.setOutputMarkupId(true);
+        uploadForm.add(downloadLink);
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        final FileUploadField fileUpload = new FileUploadField("fileUpload", new Model());
+        fileUpload.setOutputMarkupId(true);
+        fileUpload.add(new AjaxFormSubmitBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onSubmit(final AjaxRequestTarget target) {
+                final FileUpload uploadedFile = fileUpload.getFileUpload();
+                if (uploadedFile != null) {
+                    try {
+                        final byte[] uploadedBytes = uploadedFile.getBytes();
+                        final String uploaded = new String(
+                                Base64.encodeBase64(uploadedBytes),
+                                SyncopeConstants.DEFAULT_ENCODING);
+                        field.setModelObject(uploaded);
+                        target.add(field);
+
+                        final Component panelPreview = previewUtil.getPreviewer(mimeType, uploadedBytes);
+
+                        if (panelPreview != null) {
+                            changePreviewer(panelPreview);
+                        }
+
+                        fileUpload.setModelObject(null);
+                        uploadForm.addOrReplace(fileUpload);
+                        downloadLink.setEnabled(StringUtils.isNotBlank(uploaded));
+                        target.add(uploadForm);
+                    } catch (Exception e) {
+                        error(getString(Constants.ERROR) + ": " + e.getMessage());
+                        ((BaseModalPage) getPage()).getFeedbackPanel().refresh(target);
+                        LOG.error("While saving uploaded file", e);
+                    }
+                }
+            }
+        });
+
+        uploadForm.add(fileUpload);
+
+        IndicatingAjaxLink<Void> uploadLink = new IndicatingAjaxLink<Void>("uploadLink") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+            }
+        };
+        uploadForm.add(uploadLink);
+
+        IndicatingAjaxLink<Void> resetLink = new IndicatingAjaxLink<Void>("resetLink") {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                field.setModelObject(null);
+                target.add(field);
+                downloadLink.setEnabled(false);
+                container.addOrReplace(emptyFragment);
+                uploadForm.addOrReplace(container);
+                target.add(uploadForm);
+            }
+        };
+        uploadForm.add(resetLink);
+    }
+
+    private Response buildResponse() {
+        return Response.ok(new ByteArrayInputStream(Base64.decodeBase64(getModelObject()))).
+                type(StringUtils.isBlank(mimeType) ? MediaType.APPLICATION_OCTET_STREAM : mimeType).build();
+    }
+
+    private void changePreviewer(final Component panelPreview) {
+        final Fragment fragment = new Fragment("panelPreview", "previewFragment", container);
+        fragment.add(panelPreview);
+        container.addOrReplace(fragment);
+        uploadForm.addOrReplace(container);
+    }
+
+    @Override
+    public BinaryFieldPanel clone() {
+        return (BinaryFieldPanel) super.clone();
+    }
+
+    @Override
+    public FieldPanel<String> setNewModel(final IModel<String> model) {
+        field.setModel(model);
+        try {
+            final Component panelPreview = previewUtil.getPreviewer(mimeType, model.getObject());
+            if (panelPreview != null) {
+                changePreviewer(panelPreview);
+            }
+        } catch (Exception e) {
+            LOG.error("While loading saved file", e);
+        }
+        downloadLink.setEnabled(StringUtils.isNotBlank(model.getObject()));
+        uploadForm.addOrReplace(downloadLink);
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/CheckBoxMultipleChoiceFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/CheckBoxMultipleChoiceFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/CheckBoxMultipleChoiceFieldPanel.java
new file mode 100644
index 0000000..3c5119b
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/CheckBoxMultipleChoiceFieldPanel.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.wicket.markup.html.form;
+
+import java.util.List;
+
+import org.apache.wicket.markup.html.form.CheckBoxMultipleChoice;
+import org.apache.wicket.model.IModel;
+
+public class CheckBoxMultipleChoiceFieldPanel<E> extends AbstractFieldPanel<List<E>> {
+
+    private static final long serialVersionUID = 4124935025837737298L;
+
+    private final CheckBoxMultipleChoice<E> field;
+
+    public CheckBoxMultipleChoiceFieldPanel(
+            final String id, final IModel<List<E>> model, final IModel<List<E>> choices) {
+
+        super(id, model);
+
+        field = new CheckBoxMultipleChoice<E>("checkBoxMultipleChoice", model, choices);
+        add(field);
+    }
+
+    @Override
+    public AbstractFieldPanel<List<E>> setModelObject(final List<E> object) {
+        field.setModelObject(object);
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateFieldPanel.java
new file mode 100644
index 0000000..1f5aaf6
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateFieldPanel.java
@@ -0,0 +1,132 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.springframework.util.StringUtils;
+
+public class DateFieldPanel extends FieldPanel<Date> {
+
+    private static final long serialVersionUID = -428975732068281726L;
+
+    protected final String name;
+
+    protected final String datePattern;
+
+    protected DateFieldPanel(final String id, final String name, final IModel<Date> model, final String datePattern) {
+        super(id, model);
+        this.name = name;
+        this.datePattern = datePattern;
+    }
+
+    @Override
+    public FieldPanel<Date> setNewModel(final List<Serializable> list) {
+        final SimpleDateFormat formatter = datePattern == null
+                ? new SimpleDateFormat(SyncopeConstants.DEFAULT_DATE_PATTERN, Locale.getDefault())
+                : new SimpleDateFormat(datePattern, Locale.getDefault());
+
+        setNewModel(new Model<Date>() {
+
+            private static final long serialVersionUID = 527651414610325237L;
+
+            @Override
+            public Date getObject() {
+                Date date = null;
+
+                if (list != null && !list.isEmpty() && StringUtils.hasText(list.get(0).toString())) {
+                    try {
+                        // Parse string using datePattern
+                        date = formatter.parse(list.get(0).toString());
+                    } catch (ParseException e) {
+                        LOG.error("invalid parse exception", e);
+                    }
+                }
+
+                return date;
+            }
+
+            @Override
+            public void setObject(final Date object) {
+                list.clear();
+                if (object != null) {
+                    list.add(formatter.format(object));
+                }
+            }
+        });
+
+        return this;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public FieldPanel<Date> setNewModel(final ListItem item) {
+        final SimpleDateFormat formatter = datePattern == null
+                ? new SimpleDateFormat(SyncopeConstants.DEFAULT_DATE_PATTERN, Locale.getDefault())
+                : new SimpleDateFormat(datePattern, Locale.getDefault());
+
+        IModel<Date> model = new Model<Date>() {
+
+            private static final long serialVersionUID = 6799404673615637845L;
+
+            @Override
+            public Date getObject() {
+                Date date = null;
+
+                final Object obj = item.getModelObject();
+
+                if (obj != null && !obj.toString().isEmpty()) {
+                    if (obj instanceof String) {
+                        // Parse string using datePattern
+                        try {
+                            date = formatter.parse(obj.toString());
+                        } catch (ParseException e) {
+                            LOG.error("While parsing date", e);
+                        }
+                    } else if (obj instanceof Date) {
+                        // Don't parse anything
+                        date = (Date) obj;
+                    } else {
+                        // consider Long
+                        date = new Date((Long) obj);
+                    }
+                }
+
+                return date;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public void setObject(final Date object) {
+                item.setModelObject(object != null ? formatter.format(object) : null);
+            }
+        };
+
+        field.setModel(model);
+        return this;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTextFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTextFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTextFieldPanel.java
new file mode 100644
index 0000000..add3de6
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTextFieldPanel.java
@@ -0,0 +1,88 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.Date;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.datetime.markup.html.form.DateTextField;
+import org.apache.wicket.extensions.yui.calendar.DatePicker;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class DateTextFieldPanel extends DateFieldPanel {
+
+    private static final long serialVersionUID = 1919852712185883648L;
+
+    public DateTextFieldPanel(final String id, final String name, final IModel<Date> model, final String datePattern) {
+        super(id, name, model, datePattern);
+
+        field = DateTextField.forDatePattern("field", model, datePattern);
+
+        if (!isReadOnly()) {
+            field.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    // nothing to do
+                }
+            });
+        }
+
+        field.add(getDatePicker());
+
+        add(field.setLabel(new Model<String>(name)).setOutputMarkupId(true));
+    }
+
+    /**
+     * Setup a DatePicker component.
+     */
+    private DatePicker getDatePicker() {
+        final DatePicker picker = new DatePicker() {
+
+            private static final long serialVersionUID = 4166072895162221956L;
+
+            @Override
+            protected boolean enableMonthYearSelection() {
+                return true;
+            }
+        };
+
+        picker.setShowOnFieldClick(true);
+
+        return picker;
+    }
+
+    @Override
+    public FieldPanel<Date> clone() {
+        final FieldPanel<Date> panel = new DateTextFieldPanel(getId(), name, new Model<Date>(), datePattern);
+        panel.setRequired(isRequired());
+        panel.setReadOnly(isReadOnly());
+        panel.setTitle(title);
+
+        if (isRequiredLabelAdded) {
+            panel.addRequiredLabel();
+        }
+
+        return panel;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTimeFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTimeFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTimeFieldPanel.java
new file mode 100644
index 0000000..d8014f6
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/DateTimeFieldPanel.java
@@ -0,0 +1,195 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.Calendar;
+import java.util.Date;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.extensions.yui.calendar.DateTimeField;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.form.validation.AbstractFormValidator;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.validation.IValidationError;
+import org.apache.wicket.validation.ValidationError;
+
+public class DateTimeFieldPanel extends DateFieldPanel {
+
+    private static final long serialVersionUID = -428975732068281726L;
+
+    private Form form = null;
+
+    public DateTimeFieldPanel(final String id, final String name, final IModel<Date> model, final String datePattern) {
+        super(id, name, model, datePattern);
+
+        field = new DateTimeField("field", model);
+
+        final Calendar cal = Calendar.getInstance();
+
+        field.get("hours").add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                final Integer hours = ((DateTimeField) field).getHours();
+                if (hours != null) {
+                    cal.set(hours > 12 ? Calendar.HOUR_OF_DAY : Calendar.HOUR, hours);
+                    field.setModelObject(cal.getTime());
+                }
+            }
+        });
+
+        field.get("minutes").add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                final Integer minutes = ((DateTimeField) field).getMinutes();
+                if (minutes != null) {
+                    cal.set(Calendar.MINUTE, minutes);
+                    field.setModelObject(cal.getTime());
+                }
+            }
+        });
+
+        field.get("date").add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                final Date date = ((DateTimeField) field).getDate();
+                if (date == null) {
+                    field.setModelObject(null);
+                } else {
+                    cal.setTime(date);
+                    cal.set(Calendar.AM_PM, "PM".equals("" + ((DateTimeField) field).getAmOrPm()) ? Calendar.PM
+                            : Calendar.AM);
+                    field.setModelObject(cal.getTime());
+                }
+            }
+        });
+
+        field.get("amOrPmChoice").add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+            private static final long serialVersionUID = -1107858522700306810L;
+
+            @Override
+            protected void onUpdate(final AjaxRequestTarget target) {
+                cal.set(Calendar.AM_PM, "PM".equals("" + ((DateTimeField) field).getAmOrPm()) ? Calendar.PM
+                        : Calendar.AM);
+                field.setModelObject(cal.getTime());
+            }
+        });
+
+        add(field.setLabel(new Model<String>(name)).setOutputMarkupId(true));
+    }
+
+    /**
+     * Custom form validator for registering and handling DateTimeField components that are in it.
+     */
+    private class DateTimeFormValidator extends AbstractFormValidator {
+
+        private static final long serialVersionUID = 6842264694946633582L;
+
+        private FormComponent[] dateTimeComponents;
+
+        public DateTimeFormValidator(final DateTimeField dateTimeComponent) {
+            if (dateTimeComponent == null) {
+                throw new IllegalArgumentException("argument dateTimeComponent cannot be null");
+            }
+
+            dateTimeComponents = new FormComponent[] { dateTimeComponent };
+        }
+
+        @Override
+        public FormComponent[] getDependentFormComponents() {
+            return dateTimeComponents;
+        }
+
+        /**
+         * Validation rule : all 3 fields (date,hours,minutes) must be not-null.
+         *
+         * @param form
+         */
+        @Override
+        public void validate(final Form form) {
+            final DateTimeField dateTimeField = (DateTimeField) dateTimeComponents[0];
+
+            if (!(dateTimeField.getDate() != null && dateTimeField.getHours() != null
+                    && dateTimeField.getMinutes() != null)) {
+
+                ValidationError ve = new ValidationError();
+                ve.setVariables(DateTimeFormValidator.this.variablesMap());
+                ve.addKey(resourceKey());
+                dateTimeComponents[0].error((IValidationError) ve);
+            }
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    public FieldPanel<Date> setFormValidator(final Form form) {
+        if (field == null) {
+            LOG.error("Error setting form validator");
+        } else {
+            form.add(new DateTimeFormValidator(((DateTimeField) field)));
+            this.form = form;
+        }
+
+        return this;
+    }
+
+    @Override
+    public FieldPanel<Date> setStyleSheet(final String classes) {
+        field.get("date").add(AttributeModifier.replace("class", (classes == null ? "" : classes) + " date_size"));
+
+        field.get("hours").add(AttributeModifier.replace("class", classes == null ? "" : classes));
+
+        field.get("minutes").add(AttributeModifier.replace("class", classes == null ? "" : classes));
+
+        field.get("amOrPmChoice").add(AttributeModifier.replace("class", classes == null ? "" : classes));
+
+        return this;
+    }
+
+    @Override
+    public FieldPanel<Date> clone() {
+        final FieldPanel<Date> panel = new DateTimeFieldPanel(getId(), name, new Model<Date>(null), datePattern);
+
+        panel.setRequired(isRequired());
+        panel.setReadOnly(isReadOnly());
+        panel.setTitle(title);
+
+        if (isRequiredLabelAdded) {
+            panel.addRequiredLabel();
+        }
+
+        if (form != null && isRequired()) {
+            ((DateTimeFieldPanel) panel).setFormValidator(form);
+        }
+
+        return panel;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/FieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/FieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/FieldPanel.java
new file mode 100644
index 0000000..8dbc434
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/FieldPanel.java
@@ -0,0 +1,199 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.io.Serializable;
+import java.util.List;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.FormComponent;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public abstract class FieldPanel<T> extends AbstractFieldPanel<T> implements Cloneable {
+
+    private static final long serialVersionUID = -198988924922541273L;
+
+    protected FormComponent<T> field = null;
+
+    protected String title = null;
+
+    protected boolean isRequiredLabelAdded = false;
+
+    public FieldPanel(final String id, final IModel<T> model) {
+        super(id, model);
+
+        final Fragment fragment = new Fragment("required", "notRequiredFragment", this);
+        add(fragment);
+
+        setOutputMarkupId(true);
+    }
+
+    public FormComponent<T> getField() {
+        return field;
+    }
+
+    public FieldPanel<T> setTitle(final String title) {
+        this.title = title;
+        field.add(AttributeModifier.replace("title", title != null
+                ? title
+                : ""));
+
+        return this;
+    }
+
+    public FieldPanel<T> setStyleSheet(final String classes) {
+        field.add(AttributeModifier.replace("class", classes != null
+                ? classes
+                : ""));
+
+        return this;
+    }
+
+    public FieldPanel<T> setRequired(boolean required) {
+        field.setRequired(required);
+
+        return this;
+    }
+
+    public FieldPanel<T> setReadOnly(boolean readOnly) {
+        field.setEnabled(!readOnly);
+
+        return this;
+    }
+
+    public boolean isRequired() {
+        return field.isRequired();
+    }
+
+    public boolean isReadOnly() {
+        return !field.isEnabled();
+    }
+
+    public FieldPanel<T> addRequiredLabel() {
+        if (!isRequired()) {
+            setRequired(true);
+        }
+
+        final Fragment fragment = new Fragment("required", "requiredFragment", this);
+
+        fragment.add(new Label("requiredLabel", "*"));
+
+        replace(fragment);
+
+        this.isRequiredLabelAdded = true;
+
+        return this;
+    }
+
+    public FieldPanel<T> removeRequiredLabel() {
+        if (isRequired()) {
+            setRequired(false);
+        }
+
+        final Fragment fragment = new Fragment("required", "notRequiredFragment", this);
+
+        replace(fragment);
+
+        this.isRequiredLabelAdded = false;
+
+        return this;
+    }
+
+    @Override
+    public FieldPanel<T> setModelObject(final T object) {
+        field.setModelObject(object);
+        return this;
+    }
+
+    public T getModelObject() {
+        return (T) field.getModelObject();
+    }
+
+    public FieldPanel<T> setNewModel(final IModel<T> model) {
+        field.setModel(model);
+        return this;
+    }
+
+    /**
+     * Used by MultiValueSelectorPanel to attach items.
+     *
+     * @param item item to attach.
+     * @return updated FieldPanel object.
+     */
+    public FieldPanel<T> setNewModel(final ListItem<T> item) {
+        setNewModel(new IModel<T>() {
+
+            private static final long serialVersionUID = 6799404673615637845L;
+
+            @Override
+            public T getObject() {
+                return item.getModelObject();
+            }
+
+            @Override
+            public void setObject(final T object) {
+                item.setModelObject(object);
+            }
+
+            @Override
+            public void detach() {
+                // no detach
+            }
+        });
+        return this;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public FieldPanel<T> setNewModel(final List<Serializable> list) {
+        setNewModel(new Model() {
+
+            private static final long serialVersionUID = 1088212074765051906L;
+
+            @Override
+            public Serializable getObject() {
+                return list == null || list.isEmpty()
+                        ? null
+                        : list.get(0);
+            }
+
+            @Override
+            public void setObject(final Serializable object) {
+                list.clear();
+
+                if (object != null) {
+                    list.add(object);
+                }
+            }
+        });
+
+        return this;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public FieldPanel<T> clone() {
+        final FieldPanel<T> panel = SerializationUtils.clone(this);
+        panel.setModelObject(null);
+        return panel;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/LinkPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/LinkPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/LinkPanel.java
new file mode 100644
index 0000000..b31bbab
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/LinkPanel.java
@@ -0,0 +1,39 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+
+/**
+ * This empty class must exist because there not seems to be alternative to
+ * provide specialized HTML for links.
+ */
+public class LinkPanel extends Panel {
+
+    private static final long serialVersionUID = 4799005986804366330L;
+
+    public LinkPanel(final String id) {
+        super(id);
+    }
+
+    public LinkPanel(final String id, final IModel<?> model) {
+        super(id, model);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MappingPurposePanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MappingPurposePanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MappingPurposePanel.java
new file mode 100644
index 0000000..2c41f37
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MappingPurposePanel.java
@@ -0,0 +1,133 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class MappingPurposePanel extends Panel {
+
+    private static final long serialVersionUID = 322966537010107771L;
+
+    private final AjaxLink<Void> propagation;
+
+    private final AjaxLink<Void> synchronization;
+
+    private final AjaxLink<Void> both;
+
+    private final AjaxLink<Void> none;
+
+    public MappingPurposePanel(final String componentId, final IModel<MappingPurpose> model,
+            final WebMarkupContainer container) {
+
+        super(componentId, model);
+
+        propagation = new AjaxLink<Void>("propagationPurposeLink") {
+
+            private static final long serialVersionUID = -6957616042924610305L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                model.setObject(MappingPurpose.PROPAGATION);
+                setOpacity(MappingPurpose.PROPAGATION);
+                target.add(container);
+            }
+        };
+
+        synchronization = new AjaxLink<Void>("synchronizationPurposeLink") {
+
+            private static final long serialVersionUID = -6957616042924610305L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                model.setObject(MappingPurpose.SYNCHRONIZATION);
+                setOpacity(MappingPurpose.SYNCHRONIZATION);
+                target.add(container);
+            }
+        };
+
+        both = new AjaxLink<Void>("bothPurposeLink") {
+
+            private static final long serialVersionUID = -6957616042924610305L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                model.setObject(MappingPurpose.BOTH);
+                setOpacity(MappingPurpose.BOTH);
+                target.add(container);
+            }
+        };
+
+        none = new AjaxLink<Void>("nonePurposeLink") {
+
+            private static final long serialVersionUID = -6957616042924610305L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                model.setObject(MappingPurpose.NONE);
+                setOpacity(MappingPurpose.NONE);
+                target.add(container);
+            }
+        };
+
+        add(propagation);
+        add(synchronization);
+        add(both);
+        add(none);
+
+        setOpacity(model.getObject());
+    }
+
+    private void setOpacity(final MappingPurpose mappingPurpose) {
+        switch (mappingPurpose) {
+            case PROPAGATION:
+                propagation.add(new AttributeModifier("style", new Model<String>("opacity: 1;")));
+                synchronization.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                both.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                none.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                break;
+            case SYNCHRONIZATION:
+                synchronization.add(new AttributeModifier("style", new Model<String>("opacity: 1;")));
+                propagation.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                both.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                none.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                break;
+            case BOTH:
+                both.add(new AttributeModifier("style", new Model<String>("opacity: 1;")));
+                propagation.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                synchronization.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                none.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                break;
+            case NONE:
+                none.add(new AttributeModifier("style", new Model<String>("opacity: 1;")));
+                synchronization.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                propagation.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                both.add(new AttributeModifier("style", new Model<String>("opacity: 0.3;")));
+                break;
+            default:
+            // do nothing
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
new file mode 100644
index 0000000..cd4ab2a
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
@@ -0,0 +1,172 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.List;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
+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;
+
+public class MultiFieldPanel<E> extends AbstractFieldPanel<List<E>> {
+
+    private static final long serialVersionUID = -6322397761456513324L;
+
+    private ListView<E> view;
+
+    private WebMarkupContainer container;
+
+    public MultiFieldPanel(final String id, final IModel<List<E>> model, final FieldPanel<E> panelTemplate) {
+        this(id, model, panelTemplate, false);
+    }
+
+    public MultiFieldPanel(final String id, final IModel<List<E>> model, final FieldPanel<E> panelTemplate,
+            final boolean eventTemplate) {
+
+        super(id, model);
+
+        // -----------------------
+        // Object container definition
+        // -----------------------
+        container = new WebMarkupContainer("multiValueContainer");
+        container.setOutputMarkupId(true);
+        add(container);
+        // -----------------------
+
+        view = new ListView<E>("view", model) {
+
+            private static final long serialVersionUID = -9180479401817023838L;
+
+            @Override
+            protected void populateItem(final ListItem<E> item) {
+                final FieldPanel<E> fieldPanel = panelTemplate.clone();
+
+                if (eventTemplate) {
+                    fieldPanel.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                        private static final long serialVersionUID = -1107858522700306810L;
+
+                        @Override
+                        protected void onUpdate(final AjaxRequestTarget target) {
+                            send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
+                        }
+                    });
+                }
+
+		fieldPanel.getField().add(new AjaxFormComponentUpdatingBehavior(Constants.ON_BLUR) {
+                    
+                    private static final long serialVersionUID = -1107858522700306810L;
+  
+                        @Override
+                        protected void onUpdate(final AjaxRequestTarget target) {
+                        }
+                    });
+
+                fieldPanel.setNewModel(item);
+                item.add(fieldPanel);
+
+                AjaxLink<Void> minus = new IndicatingAjaxLink<Void>("drop") {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        //Drop current component
+                        model.getObject().remove(item.getModelObject());
+                        fieldPanel.getField().clearInput();
+                        target.add(container);
+                        
+                        if (eventTemplate) {
+                            send(getPage(), Broadcast.BREADTH, new MultiValueSelectorEvent(target));
+                        }
+                    }
+                };
+
+                item.add(minus);
+
+                if (model.getObject().size() <= 1) {
+                    minus.setVisible(false);
+                    minus.setEnabled(false);
+                } else {
+                    minus.setVisible(true);
+                    minus.setEnabled(true);
+                }
+
+                final Fragment fragment;
+                if (item.getIndex() == model.getObject().size() - 1) {
+                    final AjaxLink<Void> plus = new IndicatingAjaxLink<Void>("add") {
+
+                        private static final long serialVersionUID = -7978723352517770644L;
+
+                        @Override
+                        public void onClick(final AjaxRequestTarget target) {
+                            //Add current component
+                            model.getObject().add(null);
+                            target.add(container);
+                        }
+                    };
+
+                    fragment = new Fragment("panelPlus", "fragmentPlus", container);
+
+                    fragment.add(plus);
+                } else {
+                    fragment = new Fragment("panelPlus", "emptyFragment", container);
+                }
+                item.add(fragment);
+            }
+        };
+
+        container.add(view.setOutputMarkupId(true));
+        setOutputMarkupId(true);
+    }
+
+    public ListView<E> getView() {
+        return view;
+    }
+
+    public WebMarkupContainer getContainer() {
+        return container;
+    }
+
+    @Override
+    public MultiFieldPanel<E> setModelObject(final List<E> object) {
+        view.setModelObject(object);
+        return this;
+    }
+
+    public static class MultiValueSelectorEvent {
+
+        final AjaxRequestTarget target;
+
+        public MultiValueSelectorEvent(final AjaxRequestTarget target) {
+            this.target = target;
+        }
+
+        public AjaxRequestTarget getTarget() {
+            return target;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/NonI18nPalette.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/NonI18nPalette.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/NonI18nPalette.java
new file mode 100644
index 0000000..55038da
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/NonI18nPalette.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.wicket.markup.html.form;
+
+import java.util.Collection;
+import java.util.List;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.IModel;
+
+public class NonI18nPalette<T> extends Palette<T> {
+
+    private static final long serialVersionUID = 2659070187837941889L;
+
+    public NonI18nPalette(final String id,
+            final IModel<? extends List<? extends T>> model,
+            final IModel<? extends Collection<? extends T>> choicesModel,
+            final IChoiceRenderer<T> choiceRenderer, final int rows,
+            final boolean allowOrder, final boolean allowMoveAll) {
+
+        super(id, model, choicesModel, choiceRenderer, rows, allowOrder, allowMoveAll);
+    }
+
+    @Override
+    protected boolean localizeDisplayValues() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SelectableRecorder.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SelectableRecorder.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SelectableRecorder.java
new file mode 100644
index 0000000..2893533
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SelectableRecorder.java
@@ -0,0 +1,204 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.extensions.markup.html.form.palette.Palette;
+import org.apache.wicket.extensions.markup.html.form.palette.component.Recorder;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.util.string.Strings;
+
+/**
+ * A variant of Recorder, supporting single element selection (for editing purpose, for example). <b>Note</b>: this
+ * class extends Recorder<T> but in fact it is a bare copy of most source code; this was done because the original class
+ * is keeping everything private.
+ *
+ * @param <T> Type of the palette
+ */
+public class SelectableRecorder<T> extends Recorder<T> {
+
+    private static final long serialVersionUID = -3009044376132921879L;
+
+    private boolean attached = false;
+
+    private static final String[] EMPTY_IDS = new String[0];
+
+    /**
+     * Conveniently maintained array of selected ids.
+     */
+    private String[] ids;
+
+    private String selectedId;
+
+    public SelectableRecorder(final String id, final Palette<T> palette) {
+        super(id, palette);
+    }
+
+    @Override
+    protected void onBeforeRender() {
+        super.onBeforeRender();
+
+        if (!getForm().hasError()) {
+            initIds();
+        } else if (ids == null) {
+            ids = EMPTY_IDS;
+        }
+        attached = true;
+    }
+
+    /**
+     * Synchronize ids collection from the palette's model
+     */
+    private void initIds() {
+        // construct the model string based on selection collection
+        IChoiceRenderer<T> renderer = getPalette().getChoiceRenderer();
+        StringBuilder modelStringBuffer = new StringBuilder();
+        Collection<T> modelCollection = getPalette().getModelCollection();
+        if (modelCollection == null) {
+            throw new WicketRuntimeException("Expected getPalette().getModelCollection() to return a non-null value."
+                    + " Please make sure you have model object assigned to the palette");
+        }
+        Iterator<T> selection = modelCollection.iterator();
+
+        int i = 0;
+        while (selection.hasNext()) {
+            modelStringBuffer.append(renderer.getIdValue(selection.next(), i++));
+            if (selection.hasNext()) {
+                modelStringBuffer.append(",");
+            }
+        }
+
+        // set model and update ids array
+        String modelString = modelStringBuffer.toString();
+        setDefaultModel(new Model<String>(modelString));
+        updateIds(modelString);
+    }
+
+    public T getSelectedItem() {
+        if (selectedId == null) {
+            return null;
+        }
+
+        IChoiceRenderer<T> renderer = getPalette().getChoiceRenderer();
+
+        T selected = null;
+        for (T choice : getPalette().getChoices()) {
+            if (renderer.getIdValue(choice, 0).equals(selectedId)) {
+                selected = choice;
+                break;
+            }
+        }
+
+        return selected;
+    }
+
+    /**
+     * @return iterator over selected choices
+     */
+    @Override
+    public Iterator<T> getSelectedChoices() {
+        IChoiceRenderer<T> renderer = getPalette().getChoiceRenderer();
+        if (ids.length == 0) {
+            return Collections.<T>emptyList().iterator();
+        }
+
+        List<T> selected = new ArrayList<T>(ids.length);
+        for (String id : ids) {
+            for (T choice : getPalette().getChoices()) {
+                if (renderer.getIdValue(choice, 0).equals(id)) {
+                    selected.add(choice);
+                    break;
+                }
+            }
+        }
+        return selected.iterator();
+    }
+
+    /**
+     * @return iterator over unselected choices
+     */
+    @Override
+    public Iterator<T> getUnselectedChoices() {
+        IChoiceRenderer<T> renderer = getPalette().getChoiceRenderer();
+        Collection<? extends T> choices = getPalette().getChoices();
+
+        if (choices.size() - ids.length == 0) {
+            return Collections.<T>emptyList().iterator();
+        }
+
+        List<T> unselected = new ArrayList<T>(Math.max(1, choices.size() - ids.length));
+        for (T choice : choices) {
+            final String choiceId = renderer.getIdValue(choice, 0);
+            boolean selected = false;
+            for (String id : ids) {
+                if (id.equals(choiceId)) {
+                    selected = true;
+                    break;
+                }
+            }
+            if (!selected) {
+                unselected.add(choice);
+            }
+        }
+        return unselected.iterator();
+    }
+
+    @Override
+    protected void onValid() {
+        super.onValid();
+        if (attached) {
+            updateIds();
+        }
+    }
+
+    @Override
+    protected void onInvalid() {
+        super.onInvalid();
+        if (attached) {
+            updateIds();
+        }
+    }
+
+    private void updateIds() {
+        updateIds(getValue());
+    }
+
+    @Override
+    protected void updateIds(final String value) {
+        if (Strings.isEmpty(value)) {
+            ids = EMPTY_IDS;
+        } else {
+            if (value.indexOf('|') == -1) {
+                ids = value.split(",");
+                selectedId = null;
+            } else {
+                String[] splitted = value.split("\\|");
+                selectedId = splitted[0];
+                ids = splitted[1].split(",");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
new file mode 100644
index 0000000..4f71f81
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/SpinnerFieldPanel.java
@@ -0,0 +1,197 @@
+/*
+ * 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.wicket.markup.html.form;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.UUID;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.syncope.client.console.commons.Constants;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.springframework.util.StringUtils;
+
+public class SpinnerFieldPanel<T extends Number> extends FieldPanel<T> {
+
+    private static final long serialVersionUID = 6413819574530703577L;
+
+    private final String name;
+
+    private final Class<T> reference;
+
+    private final IModel<T> model;
+
+    private final T min;
+
+    private final T max;
+
+    @SuppressWarnings("unchecked")
+    public SpinnerFieldPanel(final String id, final String name, final Class<T> reference, final IModel<T> model,
+            final T min, final T max) {
+
+        super(id, model);
+        this.name = name;
+        this.reference = reference;
+        this.model = model;
+        this.min = min;
+        this.max = max;
+
+        String uuid = UUID.randomUUID().toString();
+        field = new TextField<T>("spinnerField", model, reference);
+        field.setMarkupId(uuid);
+        add(field.setLabel(new Model<String>(name)));
+
+        if (!isReadOnly()) {
+            field.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -1107858522700306810L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    // nothing to do
+                }
+            });
+        }
+
+        final StringBuilder statements = new StringBuilder();
+        statements.append("jQuery(function() {").
+                append("var spinner = $('#").append(uuid).append("').spinner();").
+                append("$('#").append(uuid).append("').spinner(").
+                append("'option', 'stop', function(event, ui) { $(this).change(); });");
+        if (this.min != null) {
+            statements.
+                    append("$('#").append(uuid).append("').spinner(").
+                    append("'option', 'min', ").append(this.min).append(");");
+        }
+        if (this.max != null) {
+            statements.
+                    append("$('#").append(uuid).append("').spinner(").
+                    append("'option', 'max', ").append(this.max).append(");");
+        }
+        statements.append("});");
+        Label spinnerFieldJS = new Label("spinnerFieldJS", statements.toString());
+        spinnerFieldJS.setEscapeModelStrings(false);
+        add(spinnerFieldJS);
+    }
+
+    @Override
+    public SpinnerFieldPanel<T> setNewModel(final List<Serializable> list) {
+        setNewModel(new Model<T>() {
+
+            private static final long serialVersionUID = 527651414610325237L;
+
+            @Override
+            public T getObject() {
+                T value = null;
+
+                if (list != null && !list.isEmpty() && StringUtils.hasText(list.get(0).toString())) {
+                    value = reference.equals(Integer.class)
+                            ? reference.cast(NumberUtils.toInt(list.get(0).toString()))
+                            : reference.equals(Long.class)
+                            ? reference.cast(NumberUtils.toLong(list.get(0).toString()))
+                            : reference.equals(Short.class)
+                            ? reference.cast(NumberUtils.toShort(list.get(0).toString()))
+                            : reference.equals(Float.class)
+                            ? reference.cast(NumberUtils.toFloat(list.get(0).toString()))
+                            : reference.equals(byte.class)
+                            ? reference.cast(NumberUtils.toByte(list.get(0).toString()))
+                            : reference.cast(NumberUtils.toDouble(list.get(0).toString()));
+                }
+
+                return value;
+            }
+
+            @Override
+            public void setObject(final T object) {
+                list.clear();
+                if (object != null) {
+                    list.add(object.toString());
+                }
+            }
+        });
+
+        return this;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override
+    public SpinnerFieldPanel<T> setNewModel(final ListItem item) {
+        field.setModel(new Model<T>() {
+
+            private static final long serialVersionUID = 6799404673615637845L;
+
+            @Override
+            public T getObject() {
+                T number = null;
+
+                final Object obj = item.getModelObject();
+
+                if (obj != null && !obj.toString().isEmpty()) {
+                    if (obj instanceof String) {
+                        number = reference.equals(Integer.class)
+                                ? reference.cast(Integer.valueOf((String) obj))
+                                : reference.equals(Long.class)
+                                ? reference.cast(Long.valueOf((String) obj))
+                                : reference.equals(Short.class)
+                                ? reference.cast(Short.valueOf((String) obj))
+                                : reference.equals(Float.class)
+                                ? reference.cast(Float.valueOf((String) obj))
+                                : reference.equals(byte.class)
+                                ? reference.cast(Byte.valueOf((String) obj))
+                                : reference.cast(Double.valueOf((String) obj));
+                    } else if (obj instanceof Number) {
+                        // Don't parse anything
+                        number = reference.cast(obj);
+                    }
+                }
+
+                return number;
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public void setObject(final T object) {
+                item.setModelObject(object == null ? null : object.toString());
+            }
+        });
+
+        return this;
+    }
+
+    @Override
+    public SpinnerFieldPanel<T> clone() {
+        SpinnerFieldPanel<T> panel = new SpinnerFieldPanel<T>(getId(), name, reference, model, min, max);
+
+        panel.setRequired(isRequired());
+        panel.setReadOnly(isReadOnly());
+        panel.setTitle(title);
+
+        if (isRequiredLabelAdded) {
+            panel.addRequiredLabel();
+        }
+
+        return panel;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/32707b3b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/AbstractBinaryPreviewer.java
----------------------------------------------------------------------
diff --git a/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/AbstractBinaryPreviewer.java b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/AbstractBinaryPreviewer.java
new file mode 100644
index 0000000..24bdb47
--- /dev/null
+++ b/syncope620/client/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/preview/AbstractBinaryPreviewer.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.wicket.markup.html.form.preview;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.markup.html.panel.Panel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractBinaryPreviewer extends Panel {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(AbstractBinaryPreviewer.class);
+
+    private static final long serialVersionUID = -2482706463911903025L;
+
+    protected final String mimeType;
+
+    protected final byte[] uploadedBytes;
+
+    public AbstractBinaryPreviewer(final String id, final String mimeType, final byte[] uploadedBytes) {
+        super(id);
+        this.mimeType = mimeType;
+        this.uploadedBytes = uploadedBytes;
+    }
+
+    public abstract Component preview();
+}