diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/AssignmentsDetailsPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/AssignmentsDetailsPanel.java index 089e63e6b3d..a5eb3ac89d7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/AssignmentsDetailsPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/AssignmentsDetailsPanel.java @@ -27,6 +27,7 @@ import com.evolveum.midpoint.gui.api.component.DisplayNamePanel; import com.evolveum.midpoint.gui.api.component.tabs.PanelTab; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangePanel; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; @@ -127,6 +128,20 @@ protected WebMarkupContainer getMultivalueContainerDetailsPanel(ListItem createDisplayNamePanel(String displayNamePanelId) { return new DisplayNamePanel<>(displayNamePanelId, Model.of(item.getModelObject().getRealValue())); } + + @Override + protected @NotNull List createTabs() { + List mappingTabs = super.createTabs(); + mappingTabs.add(new PanelTab(createStringResource("MappingRangePanel.range")) { + @Serial private static final long serialVersionUID = 1L; + + @Override + public WebMarkupContainer createPanel(String panelId) { + return new MappingRangePanel(panelId, getModel()); + } + }); + return mappingTabs; + } }; } }; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeModel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeModel.java new file mode 100644 index 00000000000..5460a6c35a6 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeModel.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.component.input.range; + +import java.io.Serial; + +import org.apache.wicket.model.IModel; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismValueWrapper; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.web.util.ExpressionUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueSetDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; + +/** + * Range of a mapping, which the schema keeps in the set of the mapping target. + * + * + * @author jjarabinec + */ +public class MappingRangeModel implements IModel { + + @Serial private static final long serialVersionUID = 1L; + + private static final Trace LOGGER = TraceManager.getTrace(MappingRangeModel.class); + + private final IModel> mappingModel; + + public MappingRangeModel(IModel> mappingModel) { + this.mappingModel = mappingModel; + } + + @Override + public ValueSetDefinitionType getObject() { + VariableBindingDefinitionType target = getTarget(); + return target != null ? target.getSet() : null; + } + + @Override + public void setObject(ValueSetDefinitionType set) { + PrismValueWrapper value = findTargetValue(); + if (value == null) { + LOGGER.error("Target of the mapping {} not found, there is nothing to store the range into", getMapping()); + return; + } + + VariableBindingDefinitionType current = value.getRealValue(); + VariableBindingDefinitionType updated = + current != null ? current.clone() : new VariableBindingDefinitionType(); + + updated.setSet(isEmpty(set) ? null : set); + value.setRealValue(updated); + } + + private static boolean isEmpty(ValueSetDefinitionType set) { + return set == null + || (set.getPredefined() == null + && ExpressionUtil.isEmpty(set.getCondition()) + && ExpressionUtil.isEmpty(set.getYieldCondition()) + && set.getAdditionalMappingSpecification().isEmpty()); + } + + private VariableBindingDefinitionType getTarget() { + MappingType mapping = getMapping(); + return mapping != null ? mapping.getTarget() : null; + } + + private MappingType getMapping() { + PrismContainerValueWrapper mappingValue = mappingModel.getObject(); + return mappingValue != null ? mappingValue.getRealValue() : null; + } + + private PrismValueWrapper findTargetValue() { + PrismContainerValueWrapper mappingValue = mappingModel.getObject(); + if (mappingValue == null) { + return null; + } + + try { + PrismPropertyWrapper wrapper = + mappingValue.findProperty(MappingType.F_TARGET); + return wrapper != null ? wrapper.getValue() : null; + } catch (SchemaException ex) { + LOGGER.warn("Couldn't find the target of the mapping {}: {}", getMapping(), ex.getMessage(), ex); + return null; + } + } + + @Override + public void detach() { + mappingModel.detach(); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeOption.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeOption.java new file mode 100644 index 00000000000..79d3e900087 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeOption.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.component.input.range; + +import com.evolveum.midpoint.web.util.ExpressionUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueSetDefinitionPredefinedType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueSetDefinitionType; + +/** + * Range of a mapping as it is offered by the GUI. + * + * @author jjarabinec + */ +public enum MappingRangeOption { + + ALL(ValueSetDefinitionPredefinedType.ALL), + MATCHING_PROVENANCE(ValueSetDefinitionPredefinedType.MATCHING_PROVENANCE), + + CONDITION(null), + + NONE(ValueSetDefinitionPredefinedType.NONE); + + private final ValueSetDefinitionPredefinedType predefined; + + MappingRangeOption(ValueSetDefinitionPredefinedType predefined) { + this.predefined = predefined; + } + + public ValueSetDefinitionPredefinedType getPredefined() { + return predefined; + } + + /** + * Reads the option out of the set of a mapping target. + * + * @param set set to read, null when the mapping has no range. + * @return the option the set stands for, or null when it stands for none of them. + */ + public static MappingRangeOption of(ValueSetDefinitionType set) { + if (set == null) { + return null; + } + + if (!ExpressionUtil.isEmpty(set.getCondition())) { + return CONDITION; + } + + ValueSetDefinitionPredefinedType predefined = set.getPredefined(); + if (predefined == null) { + return null; + } + + return switch (predefined) { + case ALL -> ALL; + case MATCHING_PROVENANCE -> MATCHING_PROVENANCE; + case NONE -> NONE; + }; + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.html new file mode 100644 index 00000000000..8b36021c830 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.html @@ -0,0 +1,26 @@ + + + + + +
+
+ +
+
+
+ + +
+
+ +
+
+
+ + + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.java new file mode 100644 index 00000000000..0cf977e533f --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangePanel.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.component.input.range; + +import java.io.Serial; +import java.util.List; + +import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.markup.html.form.EnumChoiceRenderer; +import org.apache.wicket.model.IModel; + +import com.evolveum.midpoint.gui.api.component.BasePanel; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.impl.component.input.expression.ExpressionPanel; +import com.evolveum.midpoint.web.component.input.DropDownChoicePanel; +import com.evolveum.midpoint.web.component.util.VisibleBehaviour; +import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnChangeAjaxFormUpdatingBehavior; +import com.evolveum.midpoint.web.util.ExpressionUtil; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueSetDefinitionType; + +/** + * Range of a mapping, telling which of the existing target values the mapping is in charge of. + * + * @author jjarabinec + */ +public class MappingRangePanel extends BasePanel> { + + @Serial private static final long serialVersionUID = 1L; + + private static final String ID_RANGE_CHOICE = "rangeChoice"; + private static final String ID_CONDITION = "condition"; + + private MappingRangeModel rangeModel; + + private MappingRangeOption selectedOption; + + public MappingRangePanel(String id, IModel> model) { + super(id, model); + } + + @Override + protected void onInitialize() { + super.onInitialize(); + + setOutputMarkupId(true); + rangeModel = new MappingRangeModel(getModel()); + + add(createRangeChoice()); + add(createConditionPanel()); + } + + private Component createRangeChoice() { + IModel> options = () -> MappingRangeUtils.optionsFor(getModelObject()); + + DropDownChoicePanel choice = new DropDownChoicePanel<>( + ID_RANGE_CHOICE, createOptionModel(), options, new EnumChoiceRenderer<>(this), true); + + choice.setOutputMarkupId(true); + choice.getBaseFormComponent().add(new EmptyOnChangeAjaxFormUpdatingBehavior() { + + @Override + protected void onUpdate(AjaxRequestTarget target) { + super.onUpdate(target); + target.add(MappingRangePanel.this); + } + }); + + return choice; + } + + private Component createConditionPanel() { + ExpressionPanel condition = new ExpressionPanel(ID_CONDITION, createConditionModel()) { + + @Override + protected List getChoices() { + return List.of(RecognizedEvaluator.SCRIPT, RecognizedEvaluator.FILTER); + } + }; + + condition.setEvaluatorPanelExpanded(true); + condition.add(new VisibleBehaviour(() -> MappingRangeOption.CONDITION == getSelectedOption())); + return condition; + } + + private IModel createOptionModel() { + return new IModel<>() { + + @Override + public MappingRangeOption getObject() { + return getSelectedOption(); + } + + @Override + public void setObject(MappingRangeOption option) { + selectedOption = option; + storeOption(option); + } + }; + } + + private MappingRangeOption getSelectedOption() { + return selectedOption != null ? selectedOption : MappingRangeOption.of(rangeModel.getObject()); + } + + private void storeOption(MappingRangeOption option) { + ValueSetDefinitionType updated = copyOfRange(); + + if (MappingRangeOption.CONDITION == option) { + updated.setPredefined(null); + } else { + updated.setCondition(null); + updated.setPredefined(option != null ? option.getPredefined() : null); + } + + rangeModel.setObject(updated); + } + + private IModel createConditionModel() { + return new IModel<>() { + + @Override + public ExpressionType getObject() { + ValueSetDefinitionType set = rangeModel.getObject(); + return set != null ? set.getCondition() : null; + } + + @Override + public void setObject(ExpressionType condition) { + ValueSetDefinitionType updated = copyOfRange(); + updated.setCondition(ExpressionUtil.hasEvaluatorContent(condition) ? condition : null); + rangeModel.setObject(updated); + } + }; + } + + private ValueSetDefinitionType copyOfRange() { + ValueSetDefinitionType current = rangeModel.getObject(); + return current != null ? current.clone() : new ValueSetDefinitionType(); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeUtils.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeUtils.java new file mode 100644 index 00000000000..97172f2cf1c --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/input/range/MappingRangeUtils.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.component.input.range; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.util.WebPrismUtil; +import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismContainerDefinition; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.processor.ResourceSchema; +import com.evolveum.midpoint.schema.processor.ResourceSchemaFactory; +import com.evolveum.midpoint.schema.processor.ShadowAttributeDefinition; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceAttributeDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectTypeDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueSetDefinitionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; + +/** + * Tells the panels which ranges make sense for a mapping and which one to start with. + * + * + * @author jjarabinec + */ +public class MappingRangeUtils { + + private static final Trace LOGGER = TraceManager.getTrace(MappingRangeUtils.class); + + /** + * Options to offer for the mapping. Matching the provenance needs a midPoint property to read the + * provenance from, so it is offered for a multi valued target only. + * + * @param mappingValue value of the mapping. + * @return options to offer, in the order they are shown. + */ + public static List optionsFor(PrismContainerValueWrapper mappingValue) { + boolean multiValueTarget = isMultiValueTarget(mappingValue); + + List options = new ArrayList<>(); + options.add(MappingRangeOption.ALL); + if (multiValueTarget) { + options.add(MappingRangeOption.MATCHING_PROVENANCE); + } + if (multiValueTarget || isMultiValueAttribute(mappingValue)) { + options.add(MappingRangeOption.CONDITION); + } + options.add(MappingRangeOption.NONE); + return options; + } + + /** + * Makes the range of a new mapping explicit, so that the administrator sees what the mapping is + * in charge of instead of relying on what midPoint assumes. + * + * @param mappingValue value of the new mapping. + */ + public static void initializeRange(PrismContainerValueWrapper mappingValue) { + MappingRangeModel rangeModel = new MappingRangeModel(() -> mappingValue); + if (rangeModel.getObject() == null) { + rangeModel.setObject(defaultRange(mappingValue)); + } + } + + /** + * Range a mapping should be created with. Matching the provenance is preferred wherever it can be + * used, everything else is left in charge of all the values. + * + * @param mappingValue value of the mapping. + * @return the range to store, never null. + */ + public static ValueSetDefinitionType defaultRange(PrismContainerValueWrapper mappingValue) { + MappingRangeOption option = isMultiValueTarget(mappingValue) + ? MappingRangeOption.MATCHING_PROVENANCE + : MappingRangeOption.ALL; + + return new ValueSetDefinitionType().predefined(option.getPredefined()); + } + + /** + * Tells whether the target of the mapping can hold more than one value. + * + * @param mappingValue value of the mapping. + * @return true when the target may hold more than one value. + */ + public static boolean isMultiValueTarget(PrismContainerValueWrapper mappingValue) { + ItemPath targetPath = getTargetPath(mappingValue); + if (targetPath == null) { + return false; + } + + PrismContainerDefinition focusDefinition = findFocusDefinition(mappingValue); + if (focusDefinition == null) { + return true; + } + + ItemDefinition targetDefinition = focusDefinition.findItemDefinition(targetPath); + return targetDefinition == null || targetDefinition.isMultiValue(); + } + + /** + * Tells whether the resource attribute the mapping is written under can hold more than one value. + * + * @param mappingValue value of the mapping. + * @return true when the attribute may hold more than one value. + */ + public static boolean isMultiValueAttribute(PrismContainerValueWrapper mappingValue) { + ItemPath attributePath = getAttributePath(mappingValue); + if (attributePath == null) { + return false; + } + + ResourceSchema schema = findResourceSchema(mappingValue); + if (schema == null) { + return true; + } + + PrismContainerValueWrapper objectTypeValue = + mappingValue.getParentContainerValue(ResourceObjectTypeDefinitionType.class); + + for (ShadowAttributeDefinition attribute : WebPrismUtil.searchAttributeDefinitions( + schema, objectTypeValue != null ? objectTypeValue.getRealValue() : null)) { + if (attributePath.equivalent(attribute.getItemName())) { + return attribute.isMultiValue(); + } + } + return true; + } + + private static ItemPath getAttributePath(PrismContainerValueWrapper mappingValue) { + if (mappingValue == null) { + return null; + } + + PrismContainerValueWrapper attributeValue = + mappingValue.getParentContainerValue(ResourceAttributeDefinitionType.class); + ResourceAttributeDefinitionType attribute = + attributeValue != null ? attributeValue.getRealValue() : null; + ItemPathType ref = attribute != null ? attribute.getRef() : null; + + return ref != null ? ref.getItemPath() : null; + } + + private static ResourceSchema findResourceSchema(PrismContainerValueWrapper mappingValue) { + try { + Object object = mappingValue.getParent().findObjectWrapper().getObjectOld().asObjectable(); + if (!(object instanceof ResourceType resource)) { + return null; + } + return ResourceSchemaFactory.getCompleteSchema(resource); + } catch (Exception ex) { + LOGGER.debug("Couldn't get the schema of the resource of the mapping: {}", ex.getMessage(), ex); + return null; + } + } + + private static ItemPath getTargetPath(PrismContainerValueWrapper mappingValue) { + MappingType mapping = mappingValue != null ? mappingValue.getRealValue() : null; + VariableBindingDefinitionType target = mapping != null ? mapping.getTarget() : null; + ItemPathType path = target != null ? target.getPath() : null; + if (path == null) { + return null; + } + + ItemPath itemPath = path.getItemPath().stripVariableSegment(); + return itemPath.isEmpty() ? null : itemPath; + } + + private static PrismContainerDefinition findFocusDefinition( + PrismContainerValueWrapper mappingValue) { + if (mappingValue == null) { + return null; + } + + PrismContainerValueWrapper objectTypeValue = + mappingValue.getParentContainerValue(ResourceObjectTypeDefinitionType.class); + ResourceObjectTypeDefinitionType objectType = + objectTypeValue != null ? objectTypeValue.getRealValue() : null; + + QName focusType = objectType != null && objectType.getFocus() != null + ? objectType.getFocus().getType() + : null; + + return PrismContext.get().getSchemaRegistry() + .findObjectDefinitionByType(focusType != null ? focusType : UserType.COMPLEX_TYPE); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/objecttemplate/component/ListMappingPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/objecttemplate/component/ListMappingPanel.java index 0d5cc8d3e02..683e222c19f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/objecttemplate/component/ListMappingPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/objecttemplate/component/ListMappingPanel.java @@ -10,6 +10,13 @@ import java.util.Collection; import java.util.List; +import org.apache.wicket.extensions.markup.html.tabs.ITab; +import org.apache.wicket.markup.html.WebMarkupContainer; + +import com.evolveum.midpoint.gui.api.component.tabs.PanelTab; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangePanel; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangeUtils; +import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.gui.impl.page.admin.resource.component.wizard.schemaHandling.objectType.attribute.mapping.AbstractMappingsTable; import com.evolveum.midpoint.web.component.data.column.IconColumn; @@ -253,6 +260,15 @@ public String getCssClass() { return columns; } + @Override + protected PrismContainerValueWrapper createNewValue(PrismContainerValue value, AjaxRequestTarget target) { + PrismContainerValueWrapper newValue = super.createNewValue(value, target); + if (newValue != null) { + MappingRangeUtils.initializeRange(newValue); + } + return newValue; + } + @Override protected IModel> getContainerModel() { return PrismContainerWrapperModel.fromContainerValueWrapper(getValueModel(), ObjectTemplateType.F_MAPPING); @@ -285,6 +301,19 @@ protected ItemVisibility getBasicTabVisibity(ItemWrapper itemWrapper) { } return ItemVisibility.AUTO; } + + @Override + protected List createTabs() { + List tabs = super.createTabs(); + tabs.add(new PanelTab(createStringResource("MappingRangePanel.range")) { + + @Override + public WebMarkupContainer createPanel(String panelId) { + return new MappingRangePanel(panelId, getModel()); + } + }); + return tabs; + } }; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingWizardPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingWizardPanel.java index 1868ff976dd..1cdf1fd32b9 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingWizardPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingWizardPanel.java @@ -158,6 +158,12 @@ protected void onExitPerformed(AjaxRequestTarget target) { showTableFragment(target, MappingDirection.INBOUND); } }); + steps.add(new InboundMappingRangeStepPanel(getAssignmentHolderModel(), valueModel) { + @Override + protected void onExitPerformed(AjaxRequestTarget target) { + showTableFragment(target, MappingDirection.INBOUND); + } + }); steps.add(new InboundMappingOptionalConfigurationStepPanel(getAssignmentHolderModel(), valueModel) { @Override protected void onExitPerformed(AjaxRequestTarget target) { @@ -183,6 +189,12 @@ protected void onExitPerformed(AjaxRequestTarget target) { showTableFragment(target, MappingDirection.OUTBOUND); } }); + steps.add(new OutboundMappingRangeStepPanel<>(getAssignmentHolderModel(), valueModel) { + @Override + protected void onExitPerformed(AjaxRequestTarget target) { + showTableFragment(target, MappingDirection.OUTBOUND); + } + }); steps.add(new OutboundMappingOptionalConfigurationStepPanel<>(getAssignmentHolderModel(), valueModel) { @Override protected void onExitPerformed(AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingsTable.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingsTable.java index fe061b068f9..ae2fc5c2ef1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingsTable.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/AttributeMappingsTable.java @@ -8,6 +8,7 @@ import com.evolveum.midpoint.gui.api.component.data.provider.ISelectableDataProvider; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangeUtils; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.api.util.MappingDirection; import com.evolveum.midpoint.gui.impl.component.data.column.AbstractItemWrapperColumn; @@ -47,7 +48,7 @@ public AttributeMappingsTable( @Override protected final PrismContainerValueWrapper createNewValue(PrismContainerValue value, AjaxRequestTarget target) { - return createNewVirtualMappingValue( + PrismContainerValueWrapper newValue = createNewVirtualMappingValue( value, getValueModel(), getMappingType(), @@ -55,6 +56,11 @@ protected final PrismContainerValueWrapper createNewValue(PrismCont getItemNameOfRefAttribute(), getPageBase(), target); + + if (newValue != null) { + MappingRangeUtils.initializeRange(newValue); + } + return newValue; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.html new file mode 100644 index 00000000000..fa60e9b77cb --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.html @@ -0,0 +1,17 @@ + + + + + +
+
+
+
+
+
+ + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.java new file mode 100644 index 00000000000..c522319655c --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/InboundMappingRangeStepPanel.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.page.admin.resource.component.wizard.schemaHandling.objectType.attribute.mapping; + +import java.io.Serial; + +import org.apache.wicket.model.IModel; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangePanel; +import com.evolveum.midpoint.gui.impl.component.wizard.AbstractWizardStepPanel; +import com.evolveum.midpoint.gui.impl.page.admin.resource.ResourceDetailsModel; +import com.evolveum.midpoint.web.application.PanelDisplay; +import com.evolveum.midpoint.web.application.PanelInstance; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationTypeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; + +/** + * Step configuring the range of an inbound mapping, telling which of the existing target values the + * mapping is in charge of. + * + *

The range lives in the set of the mapping target, which is a bean inside a property rather than + * a container of its own. It therefore cannot be rendered by the usual value form of the other steps + * and the step brings its own panel instead. + * + * @author jjarabinec + */ +@PanelInstance(identifier = "rw-attributes-inbound-range", + applicableForType = ResourceType.class, + applicableForOperation = OperationTypeType.WIZARD, + display = @PanelDisplay(label = "PageResource.wizard.step.attributes.inbound.range", icon = "fa fa-filter"), + expanded = true) +public class InboundMappingRangeStepPanel extends AbstractWizardStepPanel { + + @Serial private static final long serialVersionUID = 1L; + + public static final String PANEL_TYPE = "rw-attributes-inbound-range"; + + private static final String ID_RANGE = "range"; + + private final IModel> valueModel; + + public InboundMappingRangeStepPanel( + ResourceDetailsModel model, IModel> valueModel) { + super(model); + this.valueModel = valueModel; + } + + @Override + protected void onInitialize() { + super.onInitialize(); + add(new MappingRangePanel(ID_RANGE, valueModel)); + } + + @Override + public String getStepId() { + return PANEL_TYPE; + } + + @Override + public IModel getTitle() { + return createStringResource("PageResource.wizard.step.attributes.inbound.range"); + } + + @Override + protected IModel getTextModel() { + return createStringResource("PageResource.wizard.step.attributes.inbound.range.text"); + } + + @Override + protected IModel getSubTextModel() { + return createStringResource("PageResource.wizard.step.attributes.inbound.range.subText"); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.html b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.html new file mode 100644 index 00000000000..fa60e9b77cb --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.html @@ -0,0 +1,17 @@ + + + + + +

+
+
+
+
+
+ + diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.java new file mode 100644 index 00000000000..a330c2ef8a8 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/mapping/OutboundMappingRangeStepPanel.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2026 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.gui.impl.page.admin.resource.component.wizard.schemaHandling.objectType.attribute.mapping; + +import java.io.Serial; + +import org.apache.wicket.model.IModel; + +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangePanel; +import com.evolveum.midpoint.gui.impl.component.wizard.AbstractWizardStepPanel; +import com.evolveum.midpoint.gui.impl.page.admin.assignmentholder.AssignmentHolderDetailsModel; +import com.evolveum.midpoint.web.application.PanelDisplay; +import com.evolveum.midpoint.web.application.PanelInstance; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationTypeType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; + +/** + * Step configuring the range of an outbound mapping. + * + *

An outbound attribute mapping aims at the attribute it is written under rather than at an item + * named by a target path, so the choice is normally narrowed down to the whole set of values, see + * {@link com.evolveum.midpoint.gui.impl.component.input.range.MappingRangeUtils}. + * + * @author jjarabinec + */ +@PanelInstance(identifier = "rw-attributes-outbound-range", + applicableForType = ResourceType.class, + applicableForOperation = OperationTypeType.WIZARD, + display = @PanelDisplay(label = "PageResource.wizard.step.attributes.outbound.range", icon = "fa fa-filter"), + expanded = true) +public class OutboundMappingRangeStepPanel + extends AbstractWizardStepPanel { + + @Serial private static final long serialVersionUID = 1L; + + public static final String PANEL_TYPE = "rw-attributes-outbound-range"; + + private static final String ID_RANGE = "range"; + + private final IModel> valueModel; + + public OutboundMappingRangeStepPanel(AHDM model, IModel> valueModel) { + super(model); + this.valueModel = valueModel; + } + + @Override + protected void onInitialize() { + super.onInitialize(); + add(new MappingRangePanel(ID_RANGE, valueModel)); + } + + @Override + public String getStepId() { + return PANEL_TYPE; + } + + @Override + public IModel getTitle() { + return createStringResource("PageResource.wizard.step.attributes.outbound.range"); + } + + @Override + protected IModel getTextModel() { + return createStringResource("PageResource.wizard.step.attributes.outbound.range.text"); + } + + @Override + protected IModel getSubTextModel() { + return createStringResource("PageResource.wizard.step.attributes.outbound.range.subText"); + } +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/table/SmartMappingTable.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/table/SmartMappingTable.java index e8d9226ec53..19721c049d2 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/table/SmartMappingTable.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/resource/component/wizard/schemaHandling/objectType/attribute/table/SmartMappingTable.java @@ -39,6 +39,7 @@ import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.gui.api.model.LoadableModel; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangeUtils; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; import com.evolveum.midpoint.gui.api.util.MappingDirection; @@ -576,7 +577,7 @@ protected IModel> getContainerModel() { public PrismContainerValueWrapper createNewValue( @Nullable PrismContainerValue value, @Nullable AjaxRequestTarget target) { - return createNewVirtualMappingValue( + PrismContainerValueWrapper newValue = createNewVirtualMappingValue( value, getValueModel(), getMappingDirectionType(), @@ -584,6 +585,11 @@ public PrismContainerValueWrapper createNewValue( AbstractAttributeMappingsDefinitionType.F_REF, getPageBase(), target); + + if (newValue != null) { + MappingRangeUtils.initializeRange(newValue); + } + return newValue; } protected void createDuplicateValuePerform(PrismContainerValue value, AjaxRequestTarget target) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/role/component/wizard/focusMapping/FocusMappingMappingsTable.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/role/component/wizard/focusMapping/FocusMappingMappingsTable.java index f46797859f2..ae25683302b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/role/component/wizard/focusMapping/FocusMappingMappingsTable.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/page/admin/role/component/wizard/focusMapping/FocusMappingMappingsTable.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.List; +import com.evolveum.midpoint.gui.impl.component.input.range.MappingRangeUtils; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; import com.evolveum.midpoint.gui.impl.component.input.Select2MultiChoiceColumnPanel; @@ -65,6 +66,7 @@ protected PrismContainerValueWrapper createNewValue(PrismContainerValue