/* * Copyright (c) 2010-2014 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.evolveum.midpoint.web.component.wizard.resource.component.schemahandling; import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.gui.api.model.NonEmptyModel; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.util.ItemPathUtil; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; import com.evolveum.midpoint.schema.processor.ResourceSchema; import com.evolveum.midpoint.schema.processor.ResourceSchemaImpl; import com.evolveum.midpoint.schema.util.ResourceTypeUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.form.multivalue.MultiValueTextEditPanel; import com.evolveum.midpoint.web.component.form.multivalue.MultiValueTextPanel; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.component.wizard.WizardUtil; import com.evolveum.midpoint.web.component.wizard.resource.SchemaHandlingStep; import com.evolveum.midpoint.web.component.wizard.resource.component.schemahandling.modal.LimitationsEditorDialog; import com.evolveum.midpoint.web.component.wizard.resource.component.schemahandling.modal.MappingEditorDialog; import com.evolveum.midpoint.web.component.wizard.resource.dto.MappingTypeDto; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnChangeAjaxFormUpdatingBehavior; import com.evolveum.midpoint.web.page.admin.resources.PageResourceWizard; import com.evolveum.midpoint.web.page.admin.resources.PageResources; import com.evolveum.midpoint.web.util.InfoTooltipBehavior; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.wicket.RestartResponseException; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.*; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.model.*; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; import javax.xml.namespace.QName; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * @author shood * */ public class ResourceAttributeEditor extends BasePanel<ResourceAttributeDefinitionType> { private static final Trace LOGGER = TraceManager.getTrace(ResourceAttributeEditor.class); private static final String ID_LABEL = "label"; private static final String ID_SCHEMA_REF_PANEL = "schemaRefPanel"; private static final String ID_NON_SCHEMA_REF_PANEL = "nonSchemaReferencePanel"; // temporarily not used private static final String ID_REFERENCE_SELECT = "referenceSelect"; private static final String ID_REFERENCE_ALLOW = "allowRef"; // temporarily not used private static final String ID_DISPLAY_NAME = "displayName"; private static final String ID_DESCRIPTION = "description"; private static final String ID_EXCLUSIVE_STRONG = "exclusiveStrong"; private static final String ID_TOLERANT = "tolerant"; private static final String ID_TOLERANT_VP = "tolerantValuePattern"; private static final String ID_INTOLERANT_VP = "intolerantValuePattern"; private static final String ID_FETCH_STRATEGY = "fetchStrategy"; private static final String ID_MATCHING_RULE = "matchingRule"; private static final String ID_UNKNOWN_MATCHING_RULE = "unknownMatchingRule"; private static final String ID_INBOUND = "inbound"; private static final String ID_OUTBOUND_LABEL = "outboundLabel"; private static final String ID_BUTTON_OUTBOUND = "buttonOutbound"; private static final String ID_BUTTON_LIMITATIONS = "buttonLimitations"; private static final String ID_MODAL_LIMITATIONS = "limitationsEditor"; private static final String ID_MODAL_INBOUND = "inboundEditor"; private static final String ID_MODAL_OUTBOUND = "outboundEditor"; private static final String ID_T_REF = "referenceTooltip"; private static final String ID_T_ALLOW = "allowTooltip"; private static final String ID_T_LIMITATIONS = "limitationsTooltip"; private static final String ID_T_EXCLUSIVE_STRONG = "exclusiveStrongTooltip"; private static final String ID_T_TOLERANT = "tolerantTooltip"; private static final String ID_T_TOLERANT_VP = "tolerantVPTooltip"; private static final String ID_T_INTOLERANT_VP = "intolerantVPTooltip"; private static final String ID_T_FETCH = "fetchStrategyTooltip"; private static final String ID_T_MATCHING_RULE = "matchingRuleTooltip"; private static final String ID_T_OUTBOUND = "outboundTooltip"; private static final String ID_T_INBOUND = "inboundTooltip"; private static final String ID_DELETE_OUTBOUND = "deleteOutbound"; private PrismObject<ResourceType> resource; private ResourceObjectTypeDefinitionType objectType; private boolean nonSchemaRefValueAllowed = false; @NotNull final private SchemaHandlingStep parentStep; public ResourceAttributeEditor(String id, IModel<ResourceAttributeDefinitionType> model, ResourceObjectTypeDefinitionType objectType, PrismObject<ResourceType> resource, SchemaHandlingStep parentStep, NonEmptyModel<Boolean> readOnlyModel) { super(id, model); this.resource = resource; this.objectType = objectType; this.parentStep = parentStep; initLayout(readOnlyModel); } protected void initLayout(final NonEmptyModel<Boolean> readOnlyModel) { Label label = new Label(ID_LABEL, new ResourceModel("ResourceAttributeEditor.label.edit")); label.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(label); /* TEMPORARILY DISABLED QNameEditorPanel nonSchemaRefPanel = new QNameEditorPanel(ID_NON_SCHEMA_REF_PANEL, new PropertyModel<ItemPathType>(getModel(), "ref"), "SchemaHandlingStep.attribute.label.attributeName", "SchemaHandlingStep.attribute.tooltip.attributeLocalPart", "SchemaHandlingStep.attribute.label.attributeNamespace", "SchemaHandlingStep.attribute.tooltip.attributeNamespace", true, true) { @Override protected void onUpdate(AjaxRequestTarget target) { target.add(parentStep.getAttributeList()); } }; nonSchemaRefPanel.setOutputMarkupId(true); nonSchemaRefPanel.setOutputMarkupPlaceholderTag(true); nonSchemaRefPanel.add(new VisibleEnableBehaviour(){ @Override public boolean isVisible() { return nonSchemaRefValueAllowed; } }); add(nonSchemaRefPanel); */ WebMarkupContainer schemaRefPanel = new WebMarkupContainer(ID_SCHEMA_REF_PANEL); schemaRefPanel.setOutputMarkupId(true); schemaRefPanel.setOutputMarkupPlaceholderTag(true); schemaRefPanel.add(new VisibleEnableBehaviour(){ @Override public boolean isVisible() { return !nonSchemaRefValueAllowed; } @Override public boolean isEnabled() { return !readOnlyModel.getObject(); } }); add(schemaRefPanel); Label refTooltip = new Label(ID_T_REF); refTooltip.add(new InfoTooltipBehavior()); refTooltip.setOutputMarkupId(true); refTooltip.setOutputMarkupId(true); schemaRefPanel.add(refTooltip); DropDownChoice refSelect = new DropDownChoice<ItemPathType>(ID_REFERENCE_SELECT, new PropertyModel<ItemPathType>(getModel(), "ref"), new AbstractReadOnlyModel<List<ItemPathType>>() { @Override public List<ItemPathType> getObject() { return loadObjectReferences(); } }, new IChoiceRenderer<ItemPathType>() { @Override public ItemPathType getObject(String id, IModel<? extends List<? extends ItemPathType>> choices) { return StringUtils.isNotBlank(id) ? choices.getObject().get(Integer.parseInt(id)) : null; } @Override public Object getDisplayValue(ItemPathType object) { return prepareReferenceDisplayValue(object); } @Override public String getIdValue(ItemPathType object, int index) { return Integer.toString(index); } }){ @Override protected boolean isSelected(ItemPathType object, int index, String selected) { if(getModelObject() == null || getModelObject().equals(new ItemPathType())){ return false; } QName referenceQName = ItemPathUtil.getOnlySegmentQNameRobust(getModelObject()); QName optionQName = ItemPathUtil.getOnlySegmentQNameRobust(object); return ObjectUtils.equals(referenceQName, optionQName); } }; refSelect.setNullValid(false); refSelect.setOutputMarkupId(true); refSelect.setOutputMarkupPlaceholderTag(true); refSelect.add(new EmptyOnChangeAjaxFormUpdatingBehavior() { @Override protected void onUpdate(AjaxRequestTarget target) { target.add(parentStep.getAttributeList()); ((PageResourceWizard) getPageBase()).refreshIssues(target); } }); refSelect.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); schemaRefPanel.add(refSelect); /* TEMPORARILY DISABLED CheckBox allowNonSchema = new CheckBox(ID_REFERENCE_ALLOW, new PropertyModel<Boolean>(this, "nonSchemaRefValueAllowed")); allowNonSchema.add(new AjaxFormComponentUpdatingBehavior("change") { @Override protected void onUpdate(AjaxRequestTarget target) { target.add(get(ID_NON_SCHEMA_REF_PANEL), get(ID_SCHEMA_REF_PANEL)); } }); add(allowNonSchema); */ TextField displayName = new TextField<>(ID_DISPLAY_NAME, new PropertyModel<String>(getModel(), "displayName")); displayName.add(new EmptyOnChangeAjaxFormUpdatingBehavior() { @Override protected void onUpdate(AjaxRequestTarget target) { target.add(parentStep.getAttributeList()); } }); displayName.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(displayName); TextArea description = new TextArea<>(ID_DESCRIPTION, new PropertyModel<String>(getModel(), "description")); description.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(description); AjaxLink limitations = new AjaxLink(ID_BUTTON_LIMITATIONS) { @Override public void onClick(AjaxRequestTarget target) { limitationsEditPerformed(target); } }; add(limitations); CheckBox exclusiveStrong = new CheckBox(ID_EXCLUSIVE_STRONG, new PropertyModel<Boolean>(getModel(), "exclusiveStrong")); exclusiveStrong.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(exclusiveStrong); CheckBox tolerant = new CheckBox(ID_TOLERANT, new PropertyModel<Boolean>(getModel(), "tolerant")); tolerant.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(tolerant); MultiValueTextPanel tolerantVP = new MultiValueTextPanel<>(ID_TOLERANT_VP, new PropertyModel<List<String>>(getModel(), "tolerantValuePattern"), readOnlyModel, true); tolerantVP.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(tolerantVP); MultiValueTextPanel intolerantVP = new MultiValueTextPanel<>(ID_INTOLERANT_VP, new PropertyModel<List<String>>(getModel(), "intolerantValuePattern"), readOnlyModel, true); intolerantVP.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(intolerantVP); DropDownChoice fetchStrategy = new DropDownChoice<>(ID_FETCH_STRATEGY, new PropertyModel<AttributeFetchStrategyType>(getModel(), "fetchStrategy"), WebComponentUtil.createReadonlyModelFromEnum(AttributeFetchStrategyType.class), new EnumChoiceRenderer<AttributeFetchStrategyType>(this)); fetchStrategy.setNullValid(true); fetchStrategy.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); add(fetchStrategy); AttributeEditorUtils.addMatchingRuleFields(this, readOnlyModel); TextField outboundLabel = new TextField<>(ID_OUTBOUND_LABEL, new AbstractReadOnlyModel<String>() { @Override public String getObject() { ResourceAttributeDefinitionType attributeDefinition = getModel().getObject(); if(attributeDefinition == null){ return null; } return MappingTypeDto.createMappingLabel(attributeDefinition.getOutbound(), LOGGER, getPageBase().getPrismContext(), getString("MappingType.label.placeholder"), getString("MultiValueField.nameNotSpecified")); } }); outboundLabel.setEnabled(false); outboundLabel.setOutputMarkupId(true); VisibleEnableBehaviour showIfEditingOrOutboundExists = AttributeEditorUtils.createShowIfEditingOrOutboundExists(getModel(), readOnlyModel); outboundLabel.add(showIfEditingOrOutboundExists); add(outboundLabel); AjaxSubmitLink outbound = new AjaxSubmitLink(ID_BUTTON_OUTBOUND) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { outboundEditPerformed(target); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { target.add(parentStep.getPageBase().getFeedbackPanel()); } }; outbound.setOutputMarkupId(true); outbound.add(showIfEditingOrOutboundExists); add(outbound); AjaxSubmitLink deleteOutbound = new AjaxSubmitLink(ID_DELETE_OUTBOUND) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { deleteOutboundPerformed(target); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { target.add(parentStep.getPageBase().getFeedbackPanel()); } }; deleteOutbound.setOutputMarkupId(true); deleteOutbound.add(WebComponentUtil.visibleIfFalse(readOnlyModel)); add(deleteOutbound); MultiValueTextEditPanel inbound = new MultiValueTextEditPanel<MappingType>(ID_INBOUND, new PropertyModel<List<MappingType>>(getModel(), "inbound"), null, false, true, readOnlyModel) { @Override protected IModel<String> createTextModel(final IModel<MappingType> model) { return new Model<String>() { @Override public String getObject() { return MappingTypeDto.createMappingLabel(model.getObject(), LOGGER, getPageBase().getPrismContext(), getString("MappingType.label.placeholder"), getString("MultiValueField.nameNotSpecified")); } }; } @Override protected MappingType createNewEmptyItem(){ return WizardUtil.createEmptyMapping(); } @Override protected void performAddValueHook(AjaxRequestTarget target, MappingType added) { target.add(parentStep.getAttributeList()); target.add(parentStep.getAssociationList()); ((PageResourceWizard) getPageBase()).refreshIssues(target); } @Override protected void performRemoveValueHook(AjaxRequestTarget target, ListItem<MappingType> item) { target.add(parentStep.getAttributeList()); target.add(parentStep.getAssociationList()); ((PageResourceWizard) getPageBase()).refreshIssues(target); } @Override protected void editPerformed(AjaxRequestTarget target, MappingType object){ inboundEditPerformed(target, object); } }; inbound.setOutputMarkupId(true); add(inbound); Label allowTooltip = new Label(ID_T_ALLOW); allowTooltip.add(new InfoTooltipBehavior()); add(allowTooltip); Label limitationsTooltip = new Label(ID_T_LIMITATIONS); limitationsTooltip.add(new InfoTooltipBehavior()); add(limitationsTooltip); Label exclusiveStrongTooltip = new Label(ID_T_EXCLUSIVE_STRONG); exclusiveStrongTooltip.add(new InfoTooltipBehavior()); add(exclusiveStrongTooltip); Label tolerantTooltip = new Label(ID_T_TOLERANT); tolerantTooltip.add(new InfoTooltipBehavior()); add(tolerantTooltip); Label tolerantVPTooltip = new Label(ID_T_TOLERANT_VP); tolerantVPTooltip.add(new InfoTooltipBehavior()); add(tolerantVPTooltip); Label intolerantVPTooltip = new Label(ID_T_INTOLERANT_VP); intolerantVPTooltip.add(new InfoTooltipBehavior()); add(intolerantVPTooltip); Label fetchTooltip = new Label(ID_T_FETCH); fetchTooltip.add(new InfoTooltipBehavior()); add(fetchTooltip); Label matchingRuleTooltip = new Label(ID_T_MATCHING_RULE); matchingRuleTooltip.add(new InfoTooltipBehavior()); add(matchingRuleTooltip); Label outboundTooltip = new Label(ID_T_OUTBOUND); outboundTooltip.add(new InfoTooltipBehavior()); add(outboundTooltip); Label inboundTooltip = new Label(ID_T_INBOUND); inboundTooltip.add(new InfoTooltipBehavior()); add(inboundTooltip); initModals(readOnlyModel); } private void initModals(NonEmptyModel<Boolean> readOnlyModel) { ModalWindow limitationsEditor = new LimitationsEditorDialog(ID_MODAL_LIMITATIONS, new PropertyModel<List<PropertyLimitationsType>>(getModel(), "limitations"), readOnlyModel); add(limitationsEditor); ModalWindow inboundEditor = new MappingEditorDialog(ID_MODAL_INBOUND, null, readOnlyModel) { @Override public void updateComponents(AjaxRequestTarget target) { target.add(ResourceAttributeEditor.this.get(ID_INBOUND), parentStep.getAttributeList()); } }; add(inboundEditor); ModalWindow outboundEditor = new MappingEditorDialog(ID_MODAL_OUTBOUND, null, readOnlyModel) { @Override public void updateComponents(AjaxRequestTarget target) { target.add(ResourceAttributeEditor.this.get(ID_OUTBOUND_LABEL), ResourceAttributeEditor.this.get(ID_BUTTON_OUTBOUND), parentStep.getAttributeList()); } }; add(outboundEditor); } private List<ItemPathType> loadObjectReferences(){ List<ItemPathType> references = new ArrayList<>(); ResourceSchema schema = loadResourceSchema(); if (schema == null || objectType == null) { return references; } for (ObjectClassComplexTypeDefinition def: schema.getObjectClassDefinitions()) { if (objectType.getObjectClass().equals(def.getTypeName()) || objectType.getAuxiliaryObjectClass().contains(def.getTypeName())) { for (ResourceAttributeDefinition attributeDefinition : def.getAttributeDefinitions()) { ItemPath itemPath = new ItemPath(attributeDefinition.getName()); ItemPathType itemPathType = new ItemPathType(itemPath); if (!references.contains(itemPathType)) { references.add(itemPathType); } } } } Collections.sort(references, new Comparator<ItemPathType>() { @Override public int compare(ItemPathType o1, ItemPathType o2) { String s1 = prepareReferenceDisplayValue(o1); String s2 = prepareReferenceDisplayValue(o2); return String.CASE_INSENSITIVE_ORDER.compare(s1, s2); } }); return references; } private ResourceSchema loadResourceSchema() { if(resource != null){ Element xsdSchema = ResourceTypeUtil.getResourceXsdSchema(resource); if (xsdSchema == null) { return null; } try { return ResourceSchemaImpl.parse(xsdSchema, resource.toString(), getPageBase().getPrismContext()); } catch (SchemaException|RuntimeException e) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't parse resource schema.", e); getSession().error(getString("ResourceAttributeEditor.message.cantParseSchema") + " " + e.getMessage()); throw new RestartResponseException(PageResources.class); } } return null; } private String prepareReferenceDisplayValue(ItemPathType object){ if (object == null || object.getItemPath() == null) { return ""; } ItemPath path = object.getItemPath(); if (path.getSegments().size() != 1) { return path.toString(); } QName name = ItemPathUtil.getOnlySegmentQName(path); StringBuilder sb = new StringBuilder(); String prefix = SchemaConstants.NS_ICF_SCHEMA.equals(name.getNamespaceURI()) ? "icfs" : "ri"; sb.append(prefix); sb.append(": "); sb.append(name.getLocalPart()); return sb.toString(); } private void limitationsEditPerformed(AjaxRequestTarget target){ LimitationsEditorDialog window = (LimitationsEditorDialog)get(ID_MODAL_LIMITATIONS); window.show(target); } private void deleteOutboundPerformed(AjaxRequestTarget target) { ResourceAttributeDefinitionType def = getModelObject(); def.setOutbound(null); target.add(this, parentStep.getAttributeList()); } private void outboundEditPerformed(AjaxRequestTarget target){ MappingEditorDialog window = (MappingEditorDialog) get(ID_MODAL_OUTBOUND); window.updateModel(target, new PropertyModel<MappingType>(getModel(), "outbound"), false); window.show(target); } private void inboundEditPerformed(AjaxRequestTarget target, MappingType mapping){ MappingEditorDialog window = (MappingEditorDialog) get(ID_MODAL_INBOUND); window.updateModel(target, mapping, true); window.show(target); } }