/* * Copyright (c) 2010-2016 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.page.admin.users.component; import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; import com.evolveum.midpoint.gui.api.component.BasePanel; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.query.InOidFilter; import com.evolveum.midpoint.prism.query.NotFilter; import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.prism.*; import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour; import com.evolveum.midpoint.web.page.admin.configuration.component.ObjectSelectionPage; import com.evolveum.midpoint.web.page.admin.configuration.component.ObjectSelectionPanel; import com.evolveum.midpoint.web.page.admin.roles.component.UserOrgReferenceChoosePanel; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.feedback.ComponentFeedbackMessageFilter; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.PropertyModel; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Created by Kate Honchar * */ //TODO the class is created as a copy of ValueChoosePanel but //with the possibility to work with PrismPropertyValue objects // (for now ValueChoosePanel works only with PrismReferenceValue); //in future some super class is to be created to union the common // functionality of these 2 classes public class AssociationValueChoicePanel<C extends ObjectType> extends BasePanel<PrismContainerValue<ShadowAssociationType>> { private static final long serialVersionUID = 1L; private static final Trace LOGGER = TraceManager.getTrace(AssociationValueChoicePanel.class); private static final String ID_TEXT_WRAPPER = "textWrapper"; private static final String ID_TEXT = "text"; private static final String ID_FEEDBACK = "feedback"; private static final String ID_EDIT = "edit"; protected static final String MODAL_ID_OBJECT_SELECTION_POPUP = "objectSelectionPopup"; private IModel<ValueWrapper<PrismContainerValue<ShadowAssociationType>>> model; private ObjectQuery query = null; private RefinedObjectClassDefinition assocTargetDef; public AssociationValueChoicePanel(String id, IModel<ValueWrapper<PrismContainerValue<ShadowAssociationType>>> model, List<PrismPropertyValue> values, boolean required, Class<C> type, ObjectQuery query, RefinedObjectClassDefinition assocTargetDef) { super(id, (IModel)new PropertyModel<>(model, "value")); this.model = model; this.query = query; this.assocTargetDef = assocTargetDef; setOutputMarkupId(true); initLayout((IModel)new PropertyModel<>(model, "value"), values, required, type); } private void initLayout(final IModel<PrismContainerValue<ShadowAssociationType>> value, final List<PrismPropertyValue> values, final boolean required, Class<C> type) { WebMarkupContainer textWrapper = new WebMarkupContainer(ID_TEXT_WRAPPER); textWrapper.setOutputMarkupId(true); TextField<String> text = new TextField<>(ID_TEXT, createTextModel(value)); text.add(new AjaxFormComponentUpdatingBehavior("blur") { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget ajaxRequestTarget) { } }); text.setRequired(required); text.setEnabled(false); textWrapper.add(text); FeedbackPanel feedback = new FeedbackPanel(ID_FEEDBACK, new ComponentFeedbackMessageFilter(text)); feedback.setFilter(new ComponentFeedbackMessageFilter(text)); textWrapper.add(feedback); AjaxLink edit = new AjaxLink(ID_EDIT) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { editValuePerformed(target); } }; edit.add(new VisibleEnableBehaviour() { private static final long serialVersionUID = 1L; @Override public boolean isVisible() { return model.getObject().isEmpty(); } }); textWrapper.add(edit); add(textWrapper); initDialog(type, values); } protected void replace(Object object) { //TODO be careful , non systematic hack ShadowType shadowType = (ShadowType) object; ValueWrapper<PrismContainerValue<ShadowAssociationType>> oldCValueWrapper = model.getObject(); AssociationWrapper associationWrapper = (AssociationWrapper) oldCValueWrapper.getItem(); PrismContainer<ShadowAssociationType> container = (PrismContainer<ShadowAssociationType>) associationWrapper.getItem(); container.remove((PrismContainerValue)oldCValueWrapper.getValue()); PrismContainerValue<ShadowAssociationType> newValue = container.createNewValue(); ShadowAssociationType assocType = newValue.asContainerable(); ObjectReferenceType shadowRef = new ObjectReferenceType(); shadowRef.setOid(shadowType.getOid()); shadowRef.asReferenceValue().setObject(shadowType.asPrismObject()); assocType.setShadowRef(shadowRef); assocType.setName(model.getObject().getItem().getName()); getModel().setObject(newValue); LOGGER.trace("Replaced value of {} with {} ({})", this, newValue, assocType); } protected void initDialog(final Class<C> type, List<PrismPropertyValue> values) { LOGGER.debug("Initializing dialog for type {}", type); if (FocusType.class.equals(type)){ initUserOrgDialog(); } else { initGenericDialog(type, values); } } // for ModalWindow treatment see comments in ChooseTypePanel private void initGenericDialog(final Class<C> type, final List<PrismPropertyValue> values) { final ModalWindow dialog = new ModalWindow(MODAL_ID_OBJECT_SELECTION_POPUP); ObjectSelectionPanel.Context context = new ObjectSelectionPanel.Context(this) { private static final long serialVersionUID = 1L; // See analogous discussion in ChooseTypePanel public AssociationValueChoicePanel getRealParent() { return WebComponentUtil.theSameForPage(AssociationValueChoicePanel.this, getCallingPageReference()); } @Override public void chooseOperationPerformed(AjaxRequestTarget target, ObjectType object) { getRealParent().choosePerformed(target, object); } @Override public Collection<SelectorOptions<GetOperationOptions>> getDataProviderOptions(){ return getAssociationsSearchOptions(); } @Override public ObjectQuery getDataProviderQuery() { return query; } @Override public boolean isSearchEnabled() { //TODO don't commit return false; } @Override public Class<? extends ObjectType> getObjectTypeClass() { return type; } }; ObjectSelectionPage.prepareDialog(dialog, context, this, "chooseTypeDialog.title", ID_TEXT_WRAPPER); add(dialog); } private void initUserOrgDialog() { final ModalWindow dialog = new ModalWindow(MODAL_ID_OBJECT_SELECTION_POPUP); ObjectSelectionPanel.Context context = new ObjectSelectionPanel.Context(this) { private static final long serialVersionUID = 1L; // See analogous discussion in ChooseTypePanel public AssociationValueChoicePanel getRealParent() { return WebComponentUtil.theSameForPage(AssociationValueChoicePanel.this, getCallingPageReference()); } @Override public void chooseOperationPerformed(AjaxRequestTarget target, ObjectType object) { getRealParent().choosePerformed(target, object); } @Override public boolean isSearchEnabled() { return true; } @Override public Class<? extends ObjectType> getObjectTypeClass() { return UserType.class; } @Override protected WebMarkupContainer createExtraContentContainer(String extraContentId, final ObjectSelectionPanel objectSelectionPanel) { return new UserOrgReferenceChoosePanel(extraContentId, Boolean.FALSE) { @Override protected void onReferenceTypeChangePerformed(AjaxRequestTarget target, Boolean newValue) { objectSelectionPanel.updateTableByTypePerformed(target, Boolean.FALSE.equals(newValue) ? UserType.class : OrgType.class); } }; } }; ObjectSelectionPage.prepareDialog(dialog, context, this, "chooseTypeDialog.title", ID_TEXT_WRAPPER); add(dialog); } protected ObjectQuery createChooseQuery(List<PrismPropertyValue> values) { ArrayList<String> oidList = new ArrayList<>(); ObjectQuery query = new ObjectQuery(); //TODO we should add to filter currently displayed value //not to be displayed on ObjectSelectionPanel instead of saved value // for (PrismReferenceValue ref : values) { // if (ref != null) { // if (ref.getOid() != null && !ref.getOid().isEmpty()) { // oidList.add(ref.getOid()); // } // } // } // if (isediting) { // oidList.add(orgModel.getObject().getObject().asObjectable().getOid()); // } if (oidList.isEmpty()) { return null; } ObjectFilter oidFilter = InOidFilter.createInOid(oidList); query.setFilter(NotFilter.createNot(oidFilter)); return query; } /** * @return css class for off-setting other values (not first, left to the * first there is a label) */ protected String getOffsetClass() { return "col-md-offset-4"; } protected IModel<String> createTextModel(final IModel<PrismContainerValue<ShadowAssociationType>> model) { return new AbstractReadOnlyModel<String>() { private static final long serialVersionUID = 1L; @Override public String getObject() { PrismContainerValue<ShadowAssociationType> cval = model.getObject(); if (cval == null || cval.isEmpty()) { return ""; } PrismReferenceValue shadowRef = cval.findReference(ShadowAssociationType.F_SHADOW_REF).getValue(); if (shadowRef.getObject() == null) { PrismContainer<Containerable> identifiersContainer = cval.findContainer(ShadowAssociationType.F_IDENTIFIERS); List<PrismProperty<String>> identifiers = (List) identifiersContainer.getValue().getItems(); Collection<? extends RefinedAttributeDefinition<?>> secondaryIdentifierDefs = assocTargetDef.getSecondaryIdentifiers(); for (RefinedAttributeDefinition<?> secondaryIdentifierDef: secondaryIdentifierDefs) { for (PrismProperty<String> identifier: identifiers) { if (identifier.getElementName().equals(secondaryIdentifierDef.getName())) { return identifier.getRealValue(); } } } // fallback PrismProperty<String> identifierProp = identifiers.get(0); return identifierProp.getRealValue(); } else { return shadowRef.getObject().getName().toString(); } } }; } public void editValuePerformed(AjaxRequestTarget target) { ModalWindow window = (ModalWindow) get(MODAL_ID_OBJECT_SELECTION_POPUP); window.show(target); } protected void choosePerformed(AjaxRequestTarget target, C object) { ModalWindow window = (ModalWindow) get(MODAL_ID_OBJECT_SELECTION_POPUP); window.close(target); replace(object); if (LOGGER.isTraceEnabled()) { LOGGER.trace("New object instance has been added to the model."); } } protected boolean isObjectUnique(C object) { PrismContainerValue<ShadowAssociationType> old = getModelObject(); if (old == null || old.isEmpty()){ return true; } if (old.asContainerable().getShadowRef().getOid().equals(object.getOid())) { return false; } return true; } public Component getTextComponent(){ return get(ID_TEXT_WRAPPER).get(ID_TEXT); } /** * A custom code in form of hook that can be run on event of choosing new * object with this chooser component * */ protected void choosePerformedHook(AjaxRequestTarget target, C object) { } private Collection<SelectorOptions<GetOperationOptions>> getAssociationsSearchOptions() { Collection<SelectorOptions<GetOperationOptions>> options = new ArrayList<SelectorOptions<GetOperationOptions>>(); // HACK: raw shoudl not be here. noFetch should be enough. But it does not work options.add(SelectorOptions.create(ItemPath.EMPTY_PATH, GetOperationOptions.createRaw())); options.add(SelectorOptions.create(ItemPath.EMPTY_PATH, GetOperationOptions.createNoFetch())); return options; } //TODO move query creating code from PrismValuePanel private ObjectQuery getAssociationsSearchQuery() { return new ObjectQuery(); } }