From 46628f6ccb00e586113fd14ea0249137ebd09c90 Mon Sep 17 00:00:00 2001 From: Marco Di Sabatino Di Diodoro Date: Fri, 31 Jan 2025 13:58:47 +0100 Subject: [PATCH] [SYNCOPE-1859] SearchPanel displays the schema keys and doesn't consider translations --- .../markup/html/form/AjaxTextFieldPanel.java | 14 +++ .../panels/search/SearchClausePanel.java | 113 +++++++++++++----- 2 files changed, 97 insertions(+), 30 deletions(-) diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxTextFieldPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxTextFieldPanel.java index d5b72430ed..a1adcbc654 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxTextFieldPanel.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AjaxTextFieldPanel.java @@ -21,6 +21,7 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.regex.Pattern; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; @@ -33,6 +34,7 @@ import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer; import org.apache.wicket.model.IModel; import org.apache.wicket.model.ResourceModel; +import org.apache.wicket.util.convert.IConverter; public class AjaxTextFieldPanel extends TextFieldPanel implements Cloneable { @@ -82,6 +84,14 @@ protected Iterator getChoices(final String input) { } }; } + + @SuppressWarnings("unchecked") + @Override + public IConverter getConverter(final Class type) { + return AjaxTextFieldPanel.this.getConverter(). + map(converter -> (IConverter)converter). + orElseGet(() -> super.getConverter(type)); + } }; setHTMLInputNotAllowed(); add(field.setLabel(new ResourceModel(name, name)).setOutputMarkupId(true)); @@ -105,6 +115,10 @@ public void setChoices(final List choices) { } } + protected Optional> getConverter() { + return Optional.empty(); + } + public FieldPanel enableJexlHelp() { questionMarkJexlHelp.setVisible(true); return this; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java index bed3245338..88cca8291b 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java @@ -22,10 +22,10 @@ import de.agilecoders.wicket.extensions.markup.html.bootstrap.form.checkbox.bootstraptoggle.BootstrapToggleConfig; import java.io.Serializable; import java.text.ParseException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; @@ -35,6 +35,7 @@ import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.panels.search.SearchClause.Comparator; import org.apache.syncope.client.console.panels.search.SearchClause.Operator; import org.apache.syncope.client.console.panels.search.SearchClause.Type; @@ -53,7 +54,6 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.GroupTO; import org.apache.syncope.common.lib.to.PlainSchemaTO; -import org.apache.syncope.common.lib.to.RelationshipTypeTO; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; @@ -77,6 +77,9 @@ import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; +import org.springframework.util.CollectionUtils; public class SearchClausePanel extends FieldPanel { @@ -122,7 +125,7 @@ default List properties() { default void setFieldAccess( FieldPanel value, AjaxTextFieldPanel property, - LoadableDetachableModel> properties) { + LoadableDetachableModel>> properties) { value.setEnabled(true); value.setModelObject(StringUtils.EMPTY); @@ -130,7 +133,7 @@ default void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); } } @@ -164,7 +167,7 @@ default void setFieldAccess( protected final LoadableDetachableModel> comparators; - protected final LoadableDetachableModel> properties; + protected final LoadableDetachableModel>> properties; protected final Fragment operatorFragment; @@ -278,46 +281,63 @@ protected List load() { private static final long serialVersionUID = 5275935387613157437L; @Override - protected List load() { + protected List> load() { if (field.getModel().getObject() == null || field.getModel().getObject().getType() == null) { return List.of(); } switch (field.getModel().getObject().getType()) { case ATTRIBUTE: - List names = new ArrayList<>(dnames.getObject().keySet()); - if (anames != null && anames.getObject() != null && !anames.getObject().isEmpty()) { - names.addAll(anames.getObject().keySet()); + Locale locale = SyncopeConsoleSession.get().getLocale(); + List> names = dnames.getObject().entrySet().stream(). + map(item -> Pair.of( + item.getKey(), + Optional.ofNullable(item.getValue().getLabel(locale)).orElse(item.getKey()))). + collect(Collectors.toList()); + if (anames != null && !CollectionUtils.isEmpty(anames.getObject())) { + names.addAll(anames.getObject().entrySet().stream(). + map(item -> Pair.of( + item.getKey(), + Optional.ofNullable(item.getValue().getLabel(locale)).orElse(item.getKey()))). + collect(Collectors.toList())); } - return names.stream().sorted().collect(Collectors.toList()); + return names.stream(). + sorted(java.util.Comparator.comparing(name -> name.getValue().toLowerCase())). + collect(Collectors.toList()); case GROUP_MEMBERSHIP: - return groupInfo.getLeft().getObject(); + return groupInfo.getLeft().getObject().stream(). + map(item -> Pair.of(item, item)).collect(Collectors.toList()); case ROLE_MEMBERSHIP: return Optional.ofNullable(roleNames). - map(r -> r.getObject().stream().sorted().collect(Collectors.toList())). + map(r -> r.getObject().stream().sorted().map(item -> Pair.of(item, item)). + collect(Collectors.toList())). orElse(List.of()); case PRIVILEGE: return Optional.ofNullable(privilegeNames). - map(p -> p.getObject().stream().sorted().collect(Collectors.toList())). + map(p -> p.getObject().stream().sorted().map(item -> Pair.of(item, item)). + collect(Collectors.toList())). orElse(List.of()); case AUX_CLASS: - return auxClassNames.getObject().stream(). - sorted().collect(Collectors.toList()); + return auxClassNames.getObject().stream().sorted(). + map(item -> Pair.of(item, item)).collect(Collectors.toList()); case RESOURCE: - return resourceNames.getObject().stream(). - sorted().collect(Collectors.toList()); + return resourceNames.getObject().stream().sorted(). + map(item -> Pair.of(item, item)).collect(Collectors.toList()); case RELATIONSHIP: - return relationshipTypeRestClient.list().stream(). - map(RelationshipTypeTO::getKey).collect(Collectors.toList()); + return relationshipTypeRestClient.list().stream().sorted(). + map(item -> Pair.of(item.getKey(), item.getKey())). + collect(Collectors.toList()); case CUSTOM: - return customizer.properties(); + return customizer.properties().stream(). + map(item -> Pair.of(item, item)). + collect(Collectors.toList()); default: return List.of(); @@ -439,10 +459,40 @@ protected void onUpdate(final AjaxRequestTarget target) { operatorContainer.add(searchButtonFragment); } - AjaxTextFieldPanel property = new AjaxTextFieldPanel( - "property", "property", new PropertyModel<>(searchClause, "property"), true); + AjaxTextFieldPanel property = new AjaxTextFieldPanel("property", "property", + new PropertyModel<>(searchClause, "property"), true) { + + private static final long serialVersionUID = -7157802546272668001L; + + @Override + protected Optional> getConverter() { + return Optional.of(new IConverter() { + + private static final long serialVersionUID = -2754107934642211828L; + + @Override + public String convertToObject(final String label, final Locale locale) throws ConversionException { + return properties.getObject().stream(). + filter(property -> property.getValue().equalsIgnoreCase(label)). + findFirst(). + map(Pair::getKey). + orElse(label); + } + + @Override + public String convertToString(final String key, final Locale locale) { + return properties.getObject().stream(). + filter(property -> property.getKey().equalsIgnoreCase(key)). + findFirst(). + map(Pair::getValue). + orElse(key); + } + }); + } + }; + property.hideLabel().setOutputMarkupId(true).setEnabled(true); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getValue).collect(Collectors.toList())); field.add(property); property.getField().add(PREVENT_DEFAULT_RETURN); @@ -457,7 +507,8 @@ protected void onEvent(final AjaxRequestTarget target) { String[] inputAsArray = property.getField().getInputAsArray(); if (ArrayUtils.isEmpty(inputAsArray)) { - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey) + .collect(Collectors.toList())); } else if (groupInfo.getRight().getObject() > Constants.MAX_GROUP_LIST_SIZE) { String inputValue = inputAsArray.length > 1 && inputAsArray[1] != null ? inputAsArray[1] @@ -621,7 +672,9 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices( + properties.getObject().stream(). + map(Pair::getValue).collect(Collectors.toList())); break; case ROLE_MEMBERSHIP: @@ -630,7 +683,7 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); break; case PRIVILEGE: @@ -639,7 +692,7 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); break; case GROUP_MEMBERSHIP: @@ -648,7 +701,7 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); break; case GROUP_MEMBER: @@ -664,7 +717,7 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); break; case RELATIONSHIP: @@ -674,7 +727,7 @@ private void setFieldAccess( // reload properties list properties.detach(); - property.setChoices(properties.getObject()); + property.setChoices(properties.getObject().stream().map(Pair::getKey).collect(Collectors.toList())); break; case CUSTOM: