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 2019/11/29 07:50:35 UTC

[syncope] 02/03: [SYNCOPE-1515] Realm selector

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

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

commit af43767bfe1b0fa840abc0a58986350b69e2f719
Author: Marco Di Sabatino Di Diodoro <ma...@tirasa.net>
AuthorDate: Fri Nov 29 07:58:04 2019 +0100

    [SYNCOPE-1515] Realm selector
---
 .../resources/ui-commons/css/syncopeUI.scss        |  19 +-
 .../client/console/panels/RealmChoicePanel.java    | 320 +++++++++++++--------
 .../client/console/panels/RealmChoicePanel.html    |  15 +-
 .../apache/syncope/fit/console/RealmsITCase.java   |  21 +-
 4 files changed, 248 insertions(+), 127 deletions(-)

diff --git a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
index 656965f..a2623c3 100644
--- a/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
+++ b/client/idrepo/common-ui/src/main/resources/META-INF/resources/ui-commons/css/syncopeUI.scss
@@ -1112,9 +1112,24 @@ div#tablehandling ul.menu li a {
   padding: 0px !important;
 }
 
+.realm-live-search .dropdown-menu li a {
+  height: 22px;
+}
 
+.realm-live-search #bs-select-1-0 {
+  display: none;
+}
 
-
+.realm-live-search.dropdown.bootstrap-select .bs-caret {
+    display: none;
+}
+.realm-live-search.bootstrap-select>.dropdown-toggle {
+    width: 50px;
+    background: transparent;
+    border: none;
+    color: #3c8dbc;
+    float: right;
+}
 
 /* Form wrappers
 ============================================================================= */
@@ -1139,4 +1154,4 @@ div#tablehandling ul.menu li a {
 
 .max_height {
   height: 100% !important;
-}
\ No newline at end of file
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
index 8743ff8..738fafb 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
@@ -25,6 +25,9 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 import de.agilecoders.wicket.core.markup.html.bootstrap.button.dropdown.DropDownButton;
 import de.agilecoders.wicket.core.markup.html.bootstrap.image.GlyphIconType;
 import de.agilecoders.wicket.core.markup.html.bootstrap.image.IconType;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.select.BootstrapSelect;
+import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.select.BootstrapSelectConfig;
+import de.agilecoders.wicket.jquery.Key;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -32,23 +35,31 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.collections4.IterableUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.rest.RealmRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.DynRealmTO;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
 import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
 import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.markup.ComponentTag;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.IChoiceRenderer;
 import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.html.panel.Panel;
+import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.ResourceModel;
@@ -57,6 +68,8 @@ public class RealmChoicePanel extends Panel {
 
     private static final long serialVersionUID = -1100228004207271270L;
 
+    private static final int REALMS_VIEW_SIZE = 15;
+
     private final PageReference pageRef;
 
     private final LoadableDetachableModel<List<Pair<String, RealmTO>>> realmTree;
@@ -65,14 +78,14 @@ public class RealmChoicePanel extends Panel {
 
     private final WebMarkupContainer container;
 
+    private Model<RealmTO> model;
+
     private final Collection<String> availableRealms;
 
     private final Map<String, Pair<RealmTO, List<RealmTO>>> tree;
 
     private final List<AbstractLink> links = new ArrayList<>();
 
-    private Model<RealmTO> model;
-
     public RealmChoicePanel(final String id, final PageReference pageRef) {
         super(id);
         this.pageRef = pageRef;
@@ -159,135 +172,218 @@ public class RealmChoicePanel extends Panel {
         label.setOutputMarkupId(true);
         container.addOrReplace(label);
 
-        final DropDownButton realms = new DropDownButton(
-                "realms", new ResourceModel("select", ""), new Model<IconType>(GlyphIconType.folderopen)) {
+        if ((realmTree.getObject().size() + dynRealmTree.getObject().size()) > REALMS_VIEW_SIZE) {
+            List<Pair<String, RealmTO>> realms = Stream.of(
+                    realmTree.getObject(),
+                    dynRealmTree.getObject().stream().map(
+                            item -> {
+                                final RealmTO realmTO = new RealmTO();
+                                realmTO.setKey(item.getKey());
+                                realmTO.setName(item.getKey());
+                                realmTO.setFullPath(item.getKey());
+                                return Pair.of(item.getKey(), realmTO);
+                            }).collect(Collectors.toList())).flatMap(Collection::stream).collect(Collectors.toList());
+
+            BootstrapSelectConfig config = new BootstrapSelectConfig().withLiveSearch(true);
+            config.put(new Key<>("styleBase", "btn"), "btn glyphicon glyphicon-folder-open");
+            BootstrapSelect<Pair<String, RealmTO>> select =
+                    new BootstrapSelect<Pair<String, RealmTO>>("realmsLiveSearch", new Model<>(), realms) {
+
+                private static final long serialVersionUID = -12358873583862012L;
+
+                @Override
+                protected boolean isDisabled(
+                        final Pair<String, RealmTO> object,
+                        final int index,
+                        final String selected) {
+                    return availableRealms.stream().anyMatch(availableRealm -> {
+                        return !SyncopeConstants.ROOT_REALM.equals(availableRealm)
+                                && !object.getValue().getFullPath().equals(availableRealm);
+                    });
+                }
+            };
 
-            private static final long serialVersionUID = -5560086780455361131L;
+            select.with(config);
+            select.setOutputMarkupId(true);
+            select.setChoiceRenderer(new IChoiceRenderer<Pair<String, RealmTO>>() {
 
-            @Override
-            protected List<AbstractLink> newSubMenuButtons(final String buttonMarkupId) {
-                RealmChoicePanel.this.links.clear();
+                private static final long serialVersionUID = 5978544741356774985L;
 
-                RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
-                        ButtonList.getButtonMarkupId(),
-                        new Model<RealmTO>(),
-                        Buttons.Type.Link,
-                        new ResourceModel("realms", "Realms")) {
+                @Override
+                public Object getDisplayValue(final Pair<String, RealmTO> object) {
+                    return object.getKey();
+                }
 
-                    private static final long serialVersionUID = -7978723352517770744L;
+                @Override
+                public String getIdValue(final Pair<String, RealmTO> object, final int index) {
+                    return object.getKey();
+                }
 
-                    @Override
-                    public void onClick(final AjaxRequestTarget target) {
-                    }
+                @Override
+                public Pair<String, RealmTO> getObject(final String id,
+                        final IModel<? extends List<? extends Pair<String, RealmTO>>> choices) {
+                    return IterableUtils.find(choices.getObject(), new Predicate<Pair<String, RealmTO>>() {
 
-                    @Override
-                    public boolean isEnabled() {
-                        return false;
-                    }
+                        @Override
+                        public boolean evaluate(final Pair<String, RealmTO> object) {
+                            return object.getKey().equals(id);
+                        }
+                    });
+                }
+            });
+            select.add(new AjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+                private static final long serialVersionUID = -6139318907146065915L;
+
+                @Override
+                protected void onUpdate(final AjaxRequestTarget target) {
+                    model.setObject(select.getModelObject().getValue());
+                    label.setDefaultModelObject(model.getObject().getFullPath());
+                    realmLabel.setDefaultModel(new ResourceModel("realmLabel", "Realm"));
+                    target.add(label);
+                    send(pageRef.getPage(), Broadcast.EXACT,
+                            new ChosenRealm<>(select.getModelObject().getValue(), target));
+                }
+            });
+            buildRealmLinks(label, realmLabel);
+            Fragment fragment = new Fragment("realmsFragment", "realmsSearchFragment", container);
+            fragment.addOrReplace(select);
+            container.addOrReplace(fragment);
+        } else {
+            final DropDownButton realms = new DropDownButton(
+                    "realms", new ResourceModel("select", ""), new Model<IconType>(GlyphIconType.folderopen)) {
 
-                    @Override
-                    protected void onComponentTag(final ComponentTag tag) {
-                        tag.put("class", "panel box box-primary box-header with-border");
-                        tag.put("style", "margin: 20px 5px 0px 5px; width: 90%");
-                    }
-                });
+                private static final long serialVersionUID = -5560086780455361131L;
 
-                for (Pair<String, RealmTO> link : realmTree.getObject()) {
-                    final RealmTO realmTO = link.getValue();
-                    RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
-                            ButtonList.getButtonMarkupId(),
-                            Model.of(realmTO),
-                            Buttons.Type.Link,
-                            new Model<>(link.getKey())) {
+                @Override
+                protected List<AbstractLink> newSubMenuButtons(final String buttonMarkupId) {
+                    buildRealmLinks(label, realmLabel);
+                    return RealmChoicePanel.this.links;
+                }
+            };
+            realms.setOutputMarkupId(true);
+            realms.setAlignment(AlignmentBehavior.Alignment.RIGHT);
+            realms.setType(Buttons.Type.Menu);
+
+            MetaDataRoleAuthorizationStrategy.authorize(realms, ENABLE, IdRepoEntitlement.REALM_LIST);
+            Fragment fragment = new Fragment("realmsFragment", "realmsListFragment", container);
+            fragment.addOrReplace(realms);
+            container.addOrReplace(fragment);
+        }
+    }
 
-                        private static final long serialVersionUID = -7978723352517770644L;
+    private void buildRealmLinks(final Label label, final Label realmLabel) {
+        RealmChoicePanel.this.links.clear();
+        RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
+                ButtonList.getButtonMarkupId(),
+                new Model<RealmTO>(),
+                Buttons.Type.Link,
+                new ResourceModel("realms", "Realms")) {
 
-                        @Override
-                        public void onClick(final AjaxRequestTarget target) {
-                            model.setObject(realmTO);
-                            label.setDefaultModelObject(model.getObject().getFullPath());
-                            realmLabel.setDefaultModel(new ResourceModel("realmLabel", "Realm"));
-                            target.add(label);
-                            send(pageRef.getPage(), Broadcast.EXACT, new ChosenRealm<>(realmTO, target));
-                        }
+            private static final long serialVersionUID = -7978723352517770744L;
 
-                        @Override
-                        public boolean isEnabled() {
-                            return availableRealms.stream().
-                                    anyMatch(availableRealm -> realmTO.getFullPath().startsWith(availableRealm));
-                        }
-                    });
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+            }
+
+            @Override
+            public boolean isEnabled() {
+                return false;
+            }
+
+            @Override
+            protected void onComponentTag(final ComponentTag tag) {
+                tag.put("class", "panel box box-primary box-header with-border");
+                tag.put("style", "margin: 20px 5px 0px 5px; width: 90%");
+            }
+        });
+
+        for (Pair<String, RealmTO> link : realmTree.getObject()) {
+            final RealmTO realmTO = link.getValue();
+            RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
+                    ButtonList.getButtonMarkupId(),
+                    Model.of(realmTO),
+                    Buttons.Type.Link,
+                    new Model<>(link.getKey())) {
+
+                private static final long serialVersionUID = -7978723352517770644L;
+
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
+                    model.setObject(realmTO);
+                    label.setDefaultModelObject(model.getObject().getFullPath());
+                    realmLabel.setDefaultModel(new ResourceModel("realmLabel", "Realm"));
+                    target.add(label);
+                    send(pageRef.getPage(), Broadcast.EXACT, new ChosenRealm<>(realmTO, target));
                 }
 
-                if (!dynRealmTree.getObject().isEmpty()) {
-                    RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
-                            ButtonList.getButtonMarkupId(),
-                            new Model<RealmTO>(),
-                            Buttons.Type.Link,
-                            new ResourceModel("dynrealms", "Dynamic Realms")) {
+                @Override
+                public boolean isEnabled() {
+                    return availableRealms.stream().
+                            anyMatch(availableRealm -> realmTO.getFullPath().startsWith(availableRealm));
+                }
+            });
+        }
 
-                        private static final long serialVersionUID = -7978723352517770744L;
+        if (!dynRealmTree.getObject().isEmpty()) {
+            RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
+                    ButtonList.getButtonMarkupId(),
+                    new Model<RealmTO>(),
+                    Buttons.Type.Link,
+                    new ResourceModel("dynrealms", "Dynamic Realms")) {
 
-                        @Override
-                        public void onClick(final AjaxRequestTarget target) {
+                private static final long serialVersionUID = -7978723352517770744L;
 
-                        }
+                @Override
+                public void onClick(final AjaxRequestTarget target) {
 
-                        @Override
-                        public boolean isEnabled() {
-                            return false;
-                        }
+                }
 
-                        @Override
-                        protected void onComponentTag(final ComponentTag tag) {
-                            tag.put("class", "panel box box-primary box-header with-border");
-                            tag.put("style", "margin: 20px 5px 0px 5px; width: 90%");
-                        }
-                    });
+                @Override
+                public boolean isEnabled() {
+                    return false;
+                }
 
-                    for (DynRealmTO dynRealmTO : dynRealmTree.getObject()) {
-                        final RealmTO realmTO = new RealmTO();
-                        realmTO.setKey(dynRealmTO.getKey());
-                        realmTO.setName(dynRealmTO.getKey());
-                        realmTO.setFullPath(dynRealmTO.getKey());
-
-                        RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
-                                ButtonList.getButtonMarkupId(),
-                                new Model<RealmTO>(),
-                                Buttons.Type.Link,
-                                new Model<>(realmTO.getKey())) {
-
-                            private static final long serialVersionUID = -7978723352517770644L;
-
-                            @Override
-                            public void onClick(final AjaxRequestTarget target) {
-                                model.setObject(realmTO);
-                                label.setDefaultModelObject(realmTO.getKey());
-                                realmLabel.setDefaultModel(new ResourceModel("dynRealmLabel", "Dynamic Realm"));
-                                target.add(label);
-                                send(pageRef.getPage(), Broadcast.EXACT, new ChosenRealm<>(realmTO, target));
-                            }
-
-                            @Override
-                            public boolean isEnabled() {
-                                return availableRealms.stream()
-                                        .anyMatch(availableRealm -> SyncopeConstants.ROOT_REALM.equals(availableRealm)
-                                        || realmTO.getKey().equals(availableRealm));
-                            }
-                        });
-                    }
+                @Override
+                protected void onComponentTag(final ComponentTag tag) {
+                    tag.put("class", "panel box box-primary box-header with-border");
+                    tag.put("style", "margin: 20px 5px 0px 5px; width: 90%");
                 }
+            });
 
-                return RealmChoicePanel.this.links;
-            }
-        };
-        realms.setOutputMarkupId(true);
-        realms.setAlignment(AlignmentBehavior.Alignment.RIGHT);
-        realms.setType(Buttons.Type.Menu);
+            for (DynRealmTO dynRealmTO : dynRealmTree.getObject()) {
+                final RealmTO realmTO = new RealmTO();
+                realmTO.setKey(dynRealmTO.getKey());
+                realmTO.setName(dynRealmTO.getKey());
+                realmTO.setFullPath(dynRealmTO.getKey());
 
-        MetaDataRoleAuthorizationStrategy.authorize(realms, ENABLE, IdRepoEntitlement.REALM_LIST);
+                RealmChoicePanel.this.links.add(new BootstrapAjaxLink<RealmTO>(
+                        ButtonList.getButtonMarkupId(),
+                        new Model<RealmTO>(),
+                        Buttons.Type.Link,
+                        new Model<>(realmTO.getKey())) {
+
+                    private static final long serialVersionUID = -7978723352517770644L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target) {
+                        model.setObject(realmTO);
+                        label.setDefaultModelObject(realmTO.getKey());
+                        realmLabel.setDefaultModel(new ResourceModel("dynRealmLabel", "Dynamic Realm"));
+                        target.add(label);
+                        send(pageRef.getPage(), Broadcast.EXACT, new ChosenRealm<>(realmTO, target));
+                    }
 
-        container.addOrReplace(realms);
+                    @Override
+                    public boolean isEnabled() {
+                        return availableRealms.stream().anyMatch(availableRealm -> {
+                            return SyncopeConstants.ROOT_REALM.equals(availableRealm)
+                                    || realmTO.getKey().equals(availableRealm);
+                        });
+                    }
+                });
+            }
+        }
     }
 
     public final RealmChoicePanel reloadRealmTree(final AjaxRequestTarget target) {
@@ -360,10 +456,6 @@ public class RealmChoicePanel extends Panel {
         return null;
     }
 
-    public List<AbstractLink> getLinks() {
-        return links;
-    }
-
     public static class ChosenRealm<T> {
 
         private final AjaxRequestTarget target;
@@ -383,4 +475,8 @@ public class RealmChoicePanel extends Panel {
             return target;
         }
     }
+
+    public List<AbstractLink> getLinks() {
+        return links;
+    }
 }
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmChoicePanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmChoicePanel.html
index 9933477..a6271bb 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmChoicePanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/RealmChoicePanel.html
@@ -22,9 +22,18 @@ under the License.
       <div class="realm-label">
         <label wicket:id="realmLabel"/>: <label wicket:id="realm"/>
       </div>
-      <div class="realm-choice">
-        <button wicket:id="realms"></button>
-      </div>
+      <span wicket:id="realmsFragment"></span>
+
+      <wicket:fragment wicket:id="realmsSearchFragment">
+        <div class="realm-choice">
+          <select wicket:id="realmsLiveSearch" class="realm-live-search"></select> 
+        </div>
+      </wicket:fragment>
+      <wicket:fragment wicket:id="realmsListFragment">
+        <div class="realm-choice">
+          <button wicket:id="realms"></button>
+        </div>
+      </wicket:fragment>
     </div>
   </wicket:panel>
 </html>
\ No newline at end of file
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
index 16b7068..ebdec1d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/RealmsITCase.java
@@ -67,10 +67,10 @@ public class RealmsITCase extends AbstractConsoleITCase {
         // remove the new realm just created
         UTILITY_UI.getTester().clickLink("body:realmsLI:realms");
 
-        UTILITY_UI.getTester().
-                executeAjaxEvent("body:content:realmChoicePanel:container:realms:btn", Constants.ON_CLICK);
         UTILITY_UI.getTester().executeAjaxEvent(
-                "body:content:realmChoicePanel:container:realms:dropdown-menu:buttons:5:button",
+                "body:content:realmChoicePanel:container:realmsFragment:realms:btn", Constants.ON_CLICK);
+        UTILITY_UI.getTester().executeAjaxEvent(
+                "body:content:realmChoicePanel:container:realmsFragment:realms:dropdown-menu:buttons:5:button",
                 Constants.ON_CLICK);
 
         UTILITY_UI.getTester().assertLabel("body:content:realmChoicePanel:container:realm", "/testRealm");
@@ -109,10 +109,11 @@ public class RealmsITCase extends AbstractConsoleITCase {
 
     @Test
     public void addUserTemplate() {
-        UTILITY_UI.getTester().
-                executeAjaxEvent("body:content:realmChoicePanel:container:realms:btn", Constants.ON_CLICK);
         UTILITY_UI.getTester().executeAjaxEvent(
-                "body:content:realmChoicePanel:container:realms:dropdown-menu:buttons:4:button",
+                "body:content:realmChoicePanel:container:realmsFragment:realms:btn",
+                Constants.ON_CLICK);
+        UTILITY_UI.getTester().executeAjaxEvent(
+                "body:content:realmChoicePanel:container:realmsFragment:realms:dropdown-menu:buttons:4:button",
                 Constants.ON_CLICK);
 
         UTILITY_UI.getTester().assertLabel("body:content:realmChoicePanel:container:realm", "/odd");
@@ -161,10 +162,10 @@ public class RealmsITCase extends AbstractConsoleITCase {
 
     @Test
     public void verifyPropagation() {
-        UTILITY_UI.getTester().
-                executeAjaxEvent("body:content:realmChoicePanel:container:realms:btn", Constants.ON_CLICK);
-        UTILITY_UI.getTester().executeAjaxEvent(
-                "body:content:realmChoicePanel:container:realms:dropdown-menu:buttons:2:button",
+        UTILITY_UI.getTester().executeAjaxEvent("body:content:realmChoicePanel:container"
+                + ":realmsFragment:realms:btn", Constants.ON_CLICK);
+        UTILITY_UI.getTester().executeAjaxEvent("body:content:realmChoicePanel:container"
+                + ":realmsFragment:realms:dropdown-menu:buttons:2:button",
                 Constants.ON_CLICK);
 
         UTILITY_UI.getTester().clickLink(