/* * 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.component.wizard.resource.component.schemahandling.modal; import com.evolveum.midpoint.gui.api.model.LoadableModel; import com.evolveum.midpoint.gui.api.model.NonEmptyModel; import com.evolveum.midpoint.gui.api.page.PageBase; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.web.component.form.CheckFormGroup; import com.evolveum.midpoint.web.component.form.TextFormGroup; import com.evolveum.midpoint.web.component.input.DropDownChoicePanel; import com.evolveum.midpoint.web.component.wizard.resource.dto.PropertyLimitationsTypeDto; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnBlurAjaxFormUpdatingBehaviour; import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnChangeAjaxFormUpdatingBehavior; import com.evolveum.midpoint.web.util.InfoTooltipBehavior; import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PropertyAccessType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PropertyLimitationsType; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.wicket.AttributeModifier; 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.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.FormComponent; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.StringResourceModel; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; /** * @author shood * */ public class LimitationsEditorDialog extends ModalWindow{ private enum ChangeState{ SKIP, FIRST, LAST } private enum PropertyAccess{ Allow, Inherit, Deny } private static final String ID_REPEATER = "repeater"; private static final String ID_LIMITATIONS_LINK = "limitationsLink"; private static final String ID_LIMITATIONS_LABEL = "limitationsLinkName"; private static final String ID_LIMITATION_DELETE = "deleteLimitation"; private static final String ID_BODY = "accountBodyContainer"; private static final String ID_LAYER_SCHEMA = "layerSchema"; private static final String ID_LAYER_MODEL = "layerModel"; private static final String ID_LAYER_PRESENTATION = "layerPresentation"; private static final String ID_ACCESS_ADD = "addAccess"; private static final String ID_ACCESS_READ = "readAccess"; private static final String ID_ACCESS_MODIFY = "modifyAccess"; private static final String ID_MIN_OCCURS = "minOccurs"; private static final String ID_MAX_OCCURS = "maxOccurs"; private static final String ID_IGNORE = "ignore"; private static final String ID_BUTTON_ADD = "addButton"; private static final String ID_BUTTON_SAVE = "saveButton"; private static final String ID_BUTTON_CANCEL = "cancelButton"; private static final String ID_MAIN_FORM = "mainForm"; private static final String ID_T_LAYERS = "layersTooltip"; private static final String ID_T_PROPERTY = "propertyAccessTooltip"; private static final String ID_LABEL_SIZE = "col-md-4"; private static final String ID_INPUT_SIZE = "col-md-8"; private ChangeState changeState = ChangeState.FIRST; private boolean initialized; private IModel<List<PropertyLimitationsTypeDto>> model; private IModel<List<PropertyLimitationsType>> inputModel; @NotNull final private NonEmptyModel<Boolean> readOnlyModel; public LimitationsEditorDialog(String id, final IModel<List<PropertyLimitationsType>> limitation, NonEmptyModel<Boolean> readOnlyModel) { super(id); this.readOnlyModel = readOnlyModel; inputModel = limitation; model = new LoadableModel<List<PropertyLimitationsTypeDto>>(false) { @Override protected List<PropertyLimitationsTypeDto> load() { return loadLimitationsModel(limitation); } }; setOutputMarkupId(true); setTitle(createStringResource("LimitationsEditorDialog.label")); showUnloadConfirmation(false); setCssClassName(ModalWindow.CSS_CLASS_GRAY); setCookieName(LimitationsEditorDialog.class.getSimpleName() + ((int) (Math.random() * 100))); setInitialWidth(600); setInitialHeight(700); setWidthUnit("px"); WebMarkupContainer content = new WebMarkupContainer(getContentId()); content.setOutputMarkupId(true); setContent(content); } private List<PropertyLimitationsTypeDto> loadLimitationsModel(IModel<List<PropertyLimitationsType>> limList){ List<PropertyLimitationsTypeDto> limitations = new ArrayList<>(); List<PropertyLimitationsType> limitationTypeList = limList.getObject(); for(PropertyLimitationsType limitation: limitationTypeList){ limitations.add(new PropertyLimitationsTypeDto(limitation)); } return limitations; } @Override protected void onBeforeRender(){ super.onBeforeRender(); if (initialized) { return; } initLayout((WebMarkupContainer) get(getContentId())); initialized = true; } public void initLayout(WebMarkupContainer content) { Form form = new Form(ID_MAIN_FORM); form.setOutputMarkupId(true); content.add(form); ListView repeater = new ListView<PropertyLimitationsTypeDto>(ID_REPEATER, model){ @Override protected void populateItem(final ListItem<PropertyLimitationsTypeDto> item){ WebMarkupContainer linkContainer = new WebMarkupContainer(ID_LIMITATIONS_LINK); linkContainer.setOutputMarkupId(true); linkContainer.add(new AttributeModifier("href", createCollapseItemId(item, true))); item.add(linkContainer); Label linkLabel = new Label(ID_LIMITATIONS_LABEL, createLimitationsLabelModel(item)); linkContainer.add(linkLabel); AjaxLink delete = new AjaxLink(ID_LIMITATION_DELETE) { @Override public void onClick(AjaxRequestTarget target) { deleteLimitationPerformed(target, item); } }; delete.add(WebComponentUtil.visibleIfFalse(readOnlyModel)); linkContainer.add(delete); WebMarkupContainer limitationBody = new WebMarkupContainer(ID_BODY); limitationBody.setOutputMarkupId(true); limitationBody.setMarkupId(createCollapseItemId(item, false).getObject()); if (changeState != ChangeState.SKIP) { limitationBody.add(new AttributeModifier("class", new AbstractReadOnlyModel<String>() { @Override public String getObject() { if (changeState == ChangeState.FIRST && item.getIndex() == 0) { return "panel-collapse collapse in"; } else if (changeState == ChangeState.LAST && item.getIndex() == (getModelObject().size()-1)) { return "panel-collapse collapse in"; } else { return "panel-collapse collapse"; } } })); } limitationBody.add(WebComponentUtil.enabledIfFalse(readOnlyModel)); item.add(limitationBody); initLimitationBody(limitationBody, item); } }; repeater.setOutputMarkupId(true); form.add(repeater); initButtons(form); } private void initButtons(Form form) { AjaxLink add = new AjaxLink(ID_BUTTON_ADD) { @Override public void onClick(AjaxRequestTarget target) { addLimitationsPerformed(target); } }; add.add(WebComponentUtil.visibleIfFalse(readOnlyModel)); form.add(add); AjaxLink cancel = new AjaxLink(ID_BUTTON_CANCEL) { @Override public void onClick(AjaxRequestTarget target) { cancelPerformed(target); } }; form.add(cancel); AjaxLink save = new AjaxLink(ID_BUTTON_SAVE) { @Override public void onClick(AjaxRequestTarget target) { savePerformed(target); } }; save.add(WebComponentUtil.visibleIfFalse(readOnlyModel)); form.add(save); } private void initLimitationBody(final WebMarkupContainer body, ListItem<PropertyLimitationsTypeDto> item) { CheckFormGroup schema = new CheckFormGroup(ID_LAYER_SCHEMA, new PropertyModel<Boolean>(item.getModelObject(), PropertyLimitationsTypeDto.F_SCHEMA), createStringResource("LimitationsEditorDialog.label.schema"), ID_LABEL_SIZE, ID_INPUT_SIZE); schema.getCheck().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(schema); CheckFormGroup model = new CheckFormGroup(ID_LAYER_MODEL, new PropertyModel<Boolean>(item.getModelObject(), PropertyLimitationsTypeDto.F_MODEL), createStringResource("LimitationsEditorDialog.label.model"), ID_LABEL_SIZE, ID_INPUT_SIZE); model.getCheck().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(model); CheckFormGroup presentation = new CheckFormGroup(ID_LAYER_PRESENTATION, new PropertyModel<Boolean>(item.getModelObject(), PropertyLimitationsTypeDto.F_PRESENTATION), createStringResource("LimitationsEditorDialog.label.presentation"), ID_LABEL_SIZE, ID_INPUT_SIZE); presentation.getCheck().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(presentation); DropDownChoicePanel add = new DropDownChoicePanel(ID_ACCESS_ADD, getAddPropertyAccessModel(item.getModel()), WebComponentUtil.createReadonlyModelFromEnum(PropertyAccess.class), false); FormComponent<PropertyAccess> addInput = add.getBaseFormComponent(); addInput.add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); addInput.add(new EmptyOnChangeAjaxFormUpdatingBehavior()); DropDownChoicePanel read = new DropDownChoicePanel(ID_ACCESS_READ, getReadPropertyAccessModel(item.getModel()), WebComponentUtil.createReadonlyModelFromEnum(PropertyAccess.class), false); FormComponent<PropertyAccess> readInput = read.getBaseFormComponent(); readInput.add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); readInput.add(new EmptyOnChangeAjaxFormUpdatingBehavior()); DropDownChoicePanel modify = new DropDownChoicePanel(ID_ACCESS_MODIFY, getModifyPropertyAccessModel(item.getModel()), WebComponentUtil.createReadonlyModelFromEnum(PropertyAccess.class), false); FormComponent<PropertyAccess> modifyInput = modify.getBaseFormComponent(); modifyInput.add(new EmptyOnBlurAjaxFormUpdatingBehaviour()); modifyInput.add(new EmptyOnChangeAjaxFormUpdatingBehavior()); body.add(add); body.add(read); body.add(modify); TextFormGroup minOccurs = new TextFormGroup(ID_MIN_OCCURS, new PropertyModel<String>(item.getModelObject(), PropertyLimitationsTypeDto.F_LIMITATION + ".minOccurs"), createStringResource("LimitationsEditorDialog.label.minOccurs"), "SchemaHandlingStep.limitations.tooltip.minOccurs", true, ID_LABEL_SIZE, ID_INPUT_SIZE, false, false); minOccurs.getField().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(minOccurs); TextFormGroup maxOccurs = new TextFormGroup(ID_MAX_OCCURS, new PropertyModel<String>(item.getModelObject(), PropertyLimitationsTypeDto.F_LIMITATION + ".maxOccurs"), createStringResource("LimitationsEditorDialog.label.maxOccurs"), "SchemaHandlingStep.limitations.tooltip.maxOccurs", true, ID_LABEL_SIZE, ID_INPUT_SIZE, false, false); maxOccurs.getField().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(maxOccurs); CheckFormGroup ignore = new CheckFormGroup(ID_IGNORE, new PropertyModel<Boolean>(item.getModelObject(), PropertyLimitationsTypeDto.F_LIMITATION + ".ignore"), createStringResource("LimitationsEditorDialog.label.ignore"), "SchemaHandlingStep.limitations.tooltip.ignore", true, ID_LABEL_SIZE, ID_INPUT_SIZE); ignore.getCheck().add(prepareAjaxOnComponentTagUpdateBehavior()); body.add(ignore); Label layersTooltip = new Label(ID_T_LAYERS); layersTooltip.add(new InfoTooltipBehavior(true) { @Override public String getModalContainer(Component component) { return body.getMarkupId(); } }); body.add(layersTooltip); Label propertyTooltip = new Label(ID_T_PROPERTY); propertyTooltip.add(new InfoTooltipBehavior(true) { @Override public String getModalContainer(Component component) { return body.getMarkupId(); } }); body.add(propertyTooltip); } private AjaxFormComponentUpdatingBehavior prepareAjaxOnComponentTagUpdateBehavior(){ return new AjaxFormComponentUpdatingBehavior("blur") { @Override protected void onUpdate(AjaxRequestTarget target) {} }; } private IModel<String> createLimitationsLabelModel(final ListItem<PropertyLimitationsTypeDto> item){ return new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder(); PropertyLimitationsTypeDto dto = item.getModelObject(); sb.append("#").append(item.getIndex()+1).append(" - "); List<LayerType> layers = new ArrayList<>(); if (dto.isModel()) { layers.add(LayerType.MODEL); } if (dto.isPresentation()) { layers.add(LayerType.PRESENTATION); } if (dto.isSchema()) { layers.add(LayerType.SCHEMA); } sb.append(StringUtils.join(layers, ", ")); sb.append(":"); if (dto.getLimitationObject().getAccess() != null) { List<String> accesses = new ArrayList<>(); PropertyAccessType access = dto.getLimitationObject().getAccess(); if (BooleanUtils.isTrue(access.isRead())) { accesses.add(getString("LimitationsEditorDialog.label.read")); } if (BooleanUtils.isTrue(access.isAdd())) { accesses.add(getString("LimitationsEditorDialog.label.add")); } if (BooleanUtils.isTrue(access.isModify())) { accesses.add(getString("LimitationsEditorDialog.label.modify")); } sb.append(StringUtils.join(accesses, ", ")); } return sb.toString(); } }; } private IModel<String> createCollapseItemId(final ListItem<PropertyLimitationsTypeDto> item, final boolean appendSelector){ return new AbstractReadOnlyModel<String>() { @Override public String getObject() { StringBuilder sb = new StringBuilder(); if (appendSelector) { sb.append("#"); } sb.append("collapse").append(item.getId()); return sb.toString(); } }; } public StringResourceModel createStringResource(String resourceKey, Object... objects) { return PageBase.createStringResourceStatic(this, resourceKey, objects); } private void addLimitationsPerformed(AjaxRequestTarget target){ changeState = ChangeState.LAST; model.getObject().add(new PropertyLimitationsTypeDto(new PropertyLimitationsType())); target.add(getContent()); } private void deleteLimitationPerformed(AjaxRequestTarget target, ListItem<PropertyLimitationsTypeDto> item){ changeState = ChangeState.SKIP; model.getObject().remove(item.getModelObject()); target.add(getContent()); } private void cancelPerformed(AjaxRequestTarget target){ close(target); } protected void savePerformed(AjaxRequestTarget target){ List<PropertyLimitationsTypeDto> list = model.getObject(); List<PropertyLimitationsType> outputList = new ArrayList<>(); for (PropertyLimitationsTypeDto dto: list) { outputList.add(dto.prepareDtoForSave()); } inputModel.setObject(outputList); close(target); } private IModel<PropertyAccess> getAddPropertyAccessModel(final IModel<PropertyLimitationsTypeDto> model){ return new IModel<PropertyAccess>() { @Override public PropertyAccess getObject() { Boolean add = model.getObject().getLimitationObject().getAccess().isAdd(); if (add == null){ return PropertyAccess.Inherit; } else if (add) { return PropertyAccess.Allow; } else { return PropertyAccess.Deny; } } @Override public void setObject(PropertyAccess propertyAccess) { if (propertyAccess.equals(PropertyAccess.Allow)) { model.getObject().getLimitationObject().getAccess().setAdd(true); } else if (propertyAccess.equals(PropertyAccess.Deny)) { model.getObject().getLimitationObject().getAccess().setAdd(false); } else { model.getObject().getLimitationObject().getAccess().setAdd(null); } } @Override public void detach() { } }; } private IModel<PropertyAccess> getReadPropertyAccessModel(final IModel<PropertyLimitationsTypeDto> model) { return new IModel<PropertyAccess>() { @Override public PropertyAccess getObject() { Boolean read = model.getObject().getLimitationObject().getAccess().isRead(); if (read == null) { return PropertyAccess.Inherit; } else if (read){ return PropertyAccess.Allow; } else { return PropertyAccess.Deny; } } @Override public void setObject(PropertyAccess propertyAccess) { if (propertyAccess.equals(PropertyAccess.Allow)) { model.getObject().getLimitationObject().getAccess().setRead(true); } else if (propertyAccess.equals(PropertyAccess.Deny)) { model.getObject().getLimitationObject().getAccess().setRead(false); } else { model.getObject().getLimitationObject().getAccess().setRead(null); } } @Override public void detach() { } }; } private IModel<PropertyAccess> getModifyPropertyAccessModel(final IModel<PropertyLimitationsTypeDto> model) { return new IModel<PropertyAccess>() { @Override public PropertyAccess getObject() { Boolean modify = model.getObject().getLimitationObject().getAccess().isModify(); if (modify == null) { return PropertyAccess.Inherit; } else if (modify) { return PropertyAccess.Allow; } else { return PropertyAccess.Deny; } } @Override public void setObject(PropertyAccess propertyAccess) { if (propertyAccess.equals(PropertyAccess.Allow)) { model.getObject().getLimitationObject().getAccess().setModify(true); } else if (propertyAccess.equals(PropertyAccess.Deny)) { model.getObject().getLimitationObject().getAccess().setModify(false); } else { model.getObject().getLimitationObject().getAccess().setModify(null); } } @Override public void detach() { } }; } }