/*
* 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;
import com.evolveum.midpoint.gui.api.model.NonEmptyLoadableModel;
import com.evolveum.midpoint.gui.api.model.NonEmptyWrapperModel;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ResourceTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommonException;
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.data.paging.NavigatorPanel;
import com.evolveum.midpoint.web.component.form.multivalue.MultiValueAutoCompleteTextPanel;
import com.evolveum.midpoint.web.component.form.multivalue.MultiValueTextEditPanel;
import com.evolveum.midpoint.web.component.input.ObjectReferenceChoiceRenderer;
import com.evolveum.midpoint.web.component.input.QNameChoiceRenderer;
import com.evolveum.midpoint.web.component.input.TriStateComboPanel;
import com.evolveum.midpoint.web.component.util.ListDataProvider;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.component.wizard.WizardStep;
import com.evolveum.midpoint.web.component.wizard.resource.component.synchronization.ConditionalSearchFilterEditor;
import com.evolveum.midpoint.web.component.wizard.resource.component.synchronization.SynchronizationExpressionEditor;
import com.evolveum.midpoint.web.component.wizard.resource.component.synchronization.SynchronizationReactionEditor;
import com.evolveum.midpoint.web.component.wizard.resource.dto.ResourceSynchronizationDto;
import com.evolveum.midpoint.web.page.admin.configuration.component.EmptyOnChangeAjaxFormUpdatingBehavior;
import com.evolveum.midpoint.web.page.admin.resources.PageResourceWizard;
import com.evolveum.midpoint.web.session.UserProfileStorage;
import com.evolveum.midpoint.web.util.ExpressionUtil;
import com.evolveum.midpoint.web.util.InfoTooltipBehavior;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.collections.CollectionUtils;
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.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.behavior.AttributeAppender;
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.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.validation.IValidator;
import org.jetbrains.annotations.NotNull;
import javax.xml.namespace.QName;
import java.util.*;
/**
* @author lazyman
* @author shood
*/
public class SynchronizationStep extends WizardStep {
private static final Trace LOGGER = TraceManager.getTrace(SynchronizationStep.class);
private static final String DOT_CLASS = SynchronizationStep.class.getName() + ".";
private static final String OPERATION_SAVE_SYNC = DOT_CLASS + "saveResourceSynchronization";
private static final String ID_TABLE_ROWS = "tableRows";
private static final String ID_OBJECT_SYNC_ROW = "objectSyncRow";
private static final String ID_OBJECT_SYNC_LINK = "objectSyncLink";
private static final String ID_OBJECT_SYNC_LABEL = "objectSyncName";
private static final String ID_OBJECT_SYNC_DELETE = "objectSyncDelete";
private static final String ID_PAGING = "objectSyncPaging";
private static final String ID_OBJECT_SYNC_ADD = "objectSyncAddButton";
private static final String ID_OBJECT_SYNC_EDITOR = "objectSyncConfig";
private static final String ID_THIRD_ROW_CONTAINER = "thirdRowContainer";
private static final String ID_EDITOR_LABEL = "editorLabel";
private static final String ID_EDITOR_NAME = "editorName";
private static final String ID_EDITOR_DESCRIPTION = "editorDescription";
private static final String ID_EDITOR_KIND = "editorKind";
private static final String ID_EDITOR_INTENT = "editorIntent";
private static final String ID_EDITOR_FOCUS = "editorFocus";
private static final String ID_EDITOR_ENABLED = "editorEnabled";
private static final String ID_EDITOR_BUTTON_CONDITION = "editorConditionButton";
private static final String ID_EDITOR_BUTTON_CONFIRMATION = "editorConfirmationButton";
private static final String ID_EDITOR_OBJECT_TEMPLATE = "editorObjectTemplate";
private static final String ID_EDITOR_RECONCILE = "editorReconcile";
private static final String ID_EDITOR_OPPORTUNISTIC = "editorOpportunistic";
private static final String ID_EDITOR_OBJECT_CLASS = "editorObjectClass";
private static final String ID_EDITOR_EDITOR_CORRELATION = "editorCorrelation";
private static final String ID_EDITOR_REACTION = "editorReaction";
private static final String ID_T_KIND = "kindTooltip";
private static final String ID_T_INTENT = "intentTooltip";
private static final String ID_T_OBJ_CLASS = "objectClassTooltip";
private static final String ID_T_FOCUS = "focusTooltip";
private static final String ID_T_ENABLED = "enabledTooltip";
private static final String ID_T_CONDITION = "conditionTooltip";
private static final String ID_T_CONFIRMATION = "confirmationTooltip";
private static final String ID_T_OBJ_TEMPLATE = "objectTemplateTooltip";
private static final String ID_T_RECONCILE = "reconcileTooltip";
private static final String ID_T_OPPORTUNISTIC = "opportunisticTooltip";
private static final String ID_T_CORRELATION = "correlationTooltip";
private static final String ID_T_REACTION = "reactionTooltip";
@NotNull private final PageResourceWizard parentPage;
@NotNull private final NonEmptyLoadableModel<PrismObject<ResourceType>> resourceModel;
@NotNull private final NonEmptyLoadableModel<ResourceSynchronizationDto> syncDtoModel;
public SynchronizationStep(@NotNull NonEmptyLoadableModel<PrismObject<ResourceType>> resourceModel, @NotNull PageResourceWizard parentPage) {
super(parentPage);
this.parentPage = parentPage;
this.resourceModel = resourceModel;
syncDtoModel = new NonEmptyLoadableModel<ResourceSynchronizationDto>(false) {
@Override
@NotNull
protected ResourceSynchronizationDto load() {
return loadResourceSynchronization();
}
};
parentPage.registerDependentModel(syncDtoModel);
initLayout();
setOutputMarkupId(true);
}
@NotNull
private ResourceSynchronizationDto loadResourceSynchronization() {
if (resourceModel.getObject().asObjectable().getSynchronization() == null) {
resourceModel.getObject().asObjectable().setSynchronization(new SynchronizationType());
}
ResourceSynchronizationDto dto = new ResourceSynchronizationDto(resourceModel.getObject().asObjectable().getSynchronization().getObjectSynchronization());
dto.setObjectClassList(loadResourceObjectClassList(resourceModel, LOGGER, parentPage.getString("SynchronizationStep.message.errorLoadingObjectSyncList")));
return dto;
}
private boolean isAnySelected() {
return syncDtoModel.getObject().getSelected() != null;
}
private void initLayout() {
final ListDataProvider<ObjectSynchronizationType> syncProvider = new ListDataProvider<>(this,
new PropertyModel<List<ObjectSynchronizationType>>(syncDtoModel, ResourceSynchronizationDto.F_OBJECT_SYNCRONIZATION_LIST));
//first row - object sync list
WebMarkupContainer tableBody = new WebMarkupContainer(ID_TABLE_ROWS);
tableBody.setOutputMarkupId(true);
add(tableBody);
//second row - ObjectSynchronizationType editor
WebMarkupContainer objectSyncEditor = new WebMarkupContainer(ID_OBJECT_SYNC_EDITOR);
objectSyncEditor.setOutputMarkupId(true);
objectSyncEditor.add(new VisibleEnableBehaviour(){
@Override
public boolean isVisible(){
return isAnySelected();
}
});
add(objectSyncEditor);
//third row - container for more specific editors
WebMarkupContainer thirdRowContainer = new WebMarkupContainer(ID_THIRD_ROW_CONTAINER);
thirdRowContainer.setOutputMarkupId(true);
add(thirdRowContainer);
DataView<ObjectSynchronizationType> syncDataView = new DataView<ObjectSynchronizationType>(ID_OBJECT_SYNC_ROW,
syncProvider, UserProfileStorage.DEFAULT_PAGING_SIZE) {
@Override
protected void populateItem(Item<ObjectSynchronizationType> item) {
final ObjectSynchronizationType syncObject = item.getModelObject();
AjaxSubmitLink link = new AjaxSubmitLink(ID_OBJECT_SYNC_LINK) {
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
editSyncObjectPerformed(target, syncObject);
}
};
item.add(link);
Label label = new Label(ID_OBJECT_SYNC_LABEL, createObjectSyncTypeDisplayModel(syncObject));
label.setOutputMarkupId(true);
link.add(label);
AjaxLink delete = new AjaxLink(ID_OBJECT_SYNC_DELETE){
@Override
public void onClick(AjaxRequestTarget target){
deleteSyncObjectPerformed(target, syncObject);
}
};
parentPage.addEditingVisibleBehavior(delete);
link.add(delete);
item.add(AttributeModifier.replace("class", new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
if (isSelected(syncObject)) {
return "success";
}
return null;
}
}));
}
};
tableBody.add(syncDataView);
NavigatorPanel navigator = new NavigatorPanel(ID_PAGING, syncDataView, true);
navigator.setOutputMarkupId(true);
navigator.setOutputMarkupPlaceholderTag(true);
add(navigator);
AjaxLink add = new AjaxLink(ID_OBJECT_SYNC_ADD) {
@Override
public void onClick(AjaxRequestTarget target) {
addSyncObjectPerformed(target);
}
};
parentPage.addEditingVisibleBehavior(add);
add(add);
initObjectSyncEditor(objectSyncEditor);
}
@Override
protected void onConfigure() {
super.onConfigure();
if (!isAnySelected()) {
insertEmptyThirdRow();
}
}
private void initObjectSyncEditor(WebMarkupContainer editor){
Label editorLabel = new Label(ID_EDITOR_LABEL, new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
if (!isAnySelected()) {
return null; // shouldn't occur
}
String name = syncDtoModel.getObject().getSelected().getName() != null ? syncDtoModel.getObject().getSelected().getName() : "";
return getString("SynchronizationStep.label.editSyncObject", name);
}
});
editorLabel.setOutputMarkupId(true);
editor.add(editorLabel);
TextField editorName = new TextField<>(ID_EDITOR_NAME, new PropertyModel<String>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".name"));
editorName.add(new UpdateNamesBehaviour());
parentPage.addEditingEnabledBehavior(editorName);
editor.add(editorName);
TextArea editorDescription = new TextArea<>(ID_EDITOR_DESCRIPTION, new PropertyModel<String>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".description"));
parentPage.addEditingEnabledBehavior(editorDescription);
editor.add(editorDescription);
DropDownChoice editorKind = new DropDownChoice<>(ID_EDITOR_KIND,
new PropertyModel<ShadowKindType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".kind"),
WebComponentUtil.createReadonlyModelFromEnum(ShadowKindType.class),
new EnumChoiceRenderer<ShadowKindType>());
editorKind.setNullValid(true);
editorKind.add(new UpdateNamesBehaviour());
parentPage.addEditingEnabledBehavior(editorKind);
editor.add(editorKind);
TextField editorIntent = new TextField<>(ID_EDITOR_INTENT, new PropertyModel<String>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".intent"));
editorIntent.add(new UpdateNamesBehaviour());
parentPage.addEditingEnabledBehavior(editorIntent);
editor.add(editorIntent);
MultiValueAutoCompleteTextPanel<QName> editorObjectClass = new MultiValueAutoCompleteTextPanel<QName>(ID_EDITOR_OBJECT_CLASS,
new PropertyModel<List<QName>>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".objectClass"), true, parentPage.getReadOnlyModel()) {
@Override
protected IModel<String> createTextModel(final IModel<QName> model) {
return new PropertyModel<>(model, "localPart");
}
@Override
protected QName createNewEmptyItem() {
return new QName("");
}
@Override
protected boolean buttonsDisabled() {
return !isAnySelected();
}
@Override
protected List<QName> createObjectList() {
return syncDtoModel.getObject().getObjectClassList();
}
@Override
protected String createAutoCompleteObjectLabel(QName object) {
return object.getLocalPart();
}
@Override
protected IValidator<String> createAutoCompleteValidator(){
return createObjectClassValidator(new AbstractReadOnlyModel<List<QName>>() {
@Override
public List<QName> getObject() {
return syncDtoModel.getObject().getObjectClassList();
}
});
}
};
parentPage.addEditingEnabledBehavior(editorObjectClass);
editor.add(editorObjectClass);
// TODO: switch to ObjectTypeSelectPanel
DropDownChoice editorFocus = new DropDownChoice<>(ID_EDITOR_FOCUS, new PropertyModel<QName>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".focusType"),
new AbstractReadOnlyModel<List<QName>>() {
@Override
public List<QName> getObject() {
return WebComponentUtil.createFocusTypeList();
}
}, new QNameChoiceRenderer());
editorFocus.setNullValid(true);
editorFocus.add(new UpdateNamesBehaviour());
parentPage.addEditingEnabledBehavior(editorFocus);
editor.add(editorFocus);
CheckBox editorEnabled = new CheckBox(ID_EDITOR_ENABLED, new PropertyModel<Boolean>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".enabled"));
parentPage.addEditingEnabledBehavior(editorEnabled);
editor.add(editorEnabled);
AjaxSubmitLink editorCondition = new AjaxSubmitLink(ID_EDITOR_BUTTON_CONDITION){
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
conditionEditPerformed(target);
}
};
addDisableClassModifier(editorCondition);
editor.add(editorCondition);
AjaxSubmitLink editorConfirmation = new AjaxSubmitLink(ID_EDITOR_BUTTON_CONFIRMATION){
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
confirmationEditPerformed(target);
}
};
addDisableClassModifier(editorConfirmation);
editor.add(editorConfirmation);
DropDownChoice editorObjectTemplate = new DropDownChoice<>(ID_EDITOR_OBJECT_TEMPLATE,
new PropertyModel<ObjectReferenceType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".objectTemplateRef"),
new AbstractReadOnlyModel<List<ObjectReferenceType>>() {
@Override
public List<ObjectReferenceType> getObject() {
return WebModelServiceUtils.createObjectReferenceList(ObjectTemplateType.class, getPageBase(), syncDtoModel.getObject().getObjectTemplateMap());
}
}, new ObjectReferenceChoiceRenderer(syncDtoModel.getObject().getObjectTemplateMap()));
editorObjectTemplate.setNullValid(true);
parentPage.addEditingEnabledBehavior(editorObjectTemplate);
editor.add(editorObjectTemplate);
CheckBox editorReconcile = new CheckBox(ID_EDITOR_RECONCILE, new PropertyModel<Boolean>(syncDtoModel,
ResourceSynchronizationDto.F_SELECTED + ".reconcile"));
parentPage.addEditingEnabledBehavior(editorReconcile);
editor.add(editorReconcile);
TriStateComboPanel opportunistic = new TriStateComboPanel(ID_EDITOR_OPPORTUNISTIC, new PropertyModel<Boolean>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".opportunistic"));
parentPage.addEditingEnabledBehavior(opportunistic);
editor.add(opportunistic);
MultiValueTextEditPanel editorCorrelation = new MultiValueTextEditPanel<ConditionalSearchFilterType>(ID_EDITOR_EDITOR_CORRELATION,
new PropertyModel<List<ConditionalSearchFilterType>>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".correlation"),
new PropertyModel<ConditionalSearchFilterType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED_CORRELATION),
false, true, parentPage.getReadOnlyModel()) {
@Override
protected IModel<String> createTextModel(final IModel<ConditionalSearchFilterType> model) {
return new Model<String>() {
@Override
public String getObject() {
StringBuilder sb = new StringBuilder();
ConditionalSearchFilterType searchFilter = model.getObject();
if(searchFilter != null && searchFilter.getDescription() != null){
sb.append(searchFilter.getDescription());
}
if(sb.toString().isEmpty()){
sb.append(getString("SynchronizationStep.label.notSpecified"));
}
return sb.toString();
}
};
}
@Override
protected ConditionalSearchFilterType createNewEmptyItem(){
return new ConditionalSearchFilterType();
}
@Override
protected void editPerformed(AjaxRequestTarget target, ConditionalSearchFilterType object) {
correlationEditPerformed(target, object);
}
@Override
protected void performAddValueHook(AjaxRequestTarget target, ConditionalSearchFilterType added) {
parentPage.refreshIssues(target);
}
@Override
protected void performRemoveValueHook(AjaxRequestTarget target, ListItem<ConditionalSearchFilterType> item) {
parentPage.refreshIssues(target);
}
@Override
protected boolean buttonsDisabled(){
return !isAnySelected();
}
};
editor.add(editorCorrelation);
MultiValueTextEditPanel editorReaction = new MultiValueTextEditPanel<SynchronizationReactionType>(ID_EDITOR_REACTION,
new PropertyModel<List<SynchronizationReactionType>>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".reaction"),
new PropertyModel<SynchronizationReactionType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED_REACTION),
false, true, parentPage.getReadOnlyModel()) {
@Override
protected IModel<String> createTextModel(final IModel<SynchronizationReactionType> model) {
return new Model<String>() {
@Override
public String getObject() {
SynchronizationReactionType reaction = model.getObject();
if (reaction == null) {
return "";
}
StringBuilder sb = new StringBuilder();
sb.append(reaction.getName() != null ? reaction.getName() + " " : "");
sb.append("(");
if (reaction.getSituation() != null) {
sb.append(reaction.getSituation());
}
if (Boolean.TRUE.equals(reaction.isSynchronize()) || !reaction.getAction().isEmpty()) {
sb.append(" -> ");
if (!reaction.getAction().isEmpty()) {
boolean first = true;
for (SynchronizationActionType action : reaction.getAction()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(StringUtils.substringAfter(action.getHandlerUri(), "#"));
}
} else {
sb.append("sync"); // TODO i18n
}
}
sb.append(")");
return sb.toString();
}
};
}
@Override
protected SynchronizationReactionType createNewEmptyItem(){
return new SynchronizationReactionType();
}
@Override
protected void editPerformed(AjaxRequestTarget target, SynchronizationReactionType object){
reactionEditPerformed(target, object);
}
@Override
protected void performAddValueHook(AjaxRequestTarget target, SynchronizationReactionType added) {
parentPage.refreshIssues(target);
}
@Override
protected void performRemoveValueHook(AjaxRequestTarget target, ListItem<SynchronizationReactionType> item) {
parentPage.refreshIssues(target);
}
@Override
protected boolean buttonsDisabled(){
return !isAnySelected();
}
};
editorReaction.setOutputMarkupId(true);
editor.add(editorReaction);
Label kindTooltip = new Label(ID_T_KIND);
kindTooltip.add(new InfoTooltipBehavior());
editor.add(kindTooltip);
Label intentTooltip = new Label(ID_T_INTENT);
intentTooltip.add(new InfoTooltipBehavior());
editor.add(intentTooltip);
Label objClassTooltip = new Label(ID_T_OBJ_CLASS);
objClassTooltip.add(new InfoTooltipBehavior());
editor.add(objClassTooltip);
Label focusTooltip = new Label(ID_T_FOCUS);
focusTooltip.add(new InfoTooltipBehavior());
editor.add(focusTooltip);
Label enabledTooltip = new Label(ID_T_ENABLED);
enabledTooltip.add(new InfoTooltipBehavior());
editor.add(enabledTooltip);
Label conditionTooltip = new Label(ID_T_CONDITION);
conditionTooltip.add(new InfoTooltipBehavior());
editor.add(conditionTooltip);
Label confirmationTooltip = new Label(ID_T_CONFIRMATION);
confirmationTooltip.add(new InfoTooltipBehavior());
editor.add(confirmationTooltip);
Label objTemplateTooltip = new Label(ID_T_OBJ_TEMPLATE);
objTemplateTooltip.add(new InfoTooltipBehavior());
editor.add(objTemplateTooltip);
Label reconcileTooltip = new Label(ID_T_RECONCILE);
reconcileTooltip.add(new InfoTooltipBehavior());
editor.add(reconcileTooltip);
Label opportunisticTooltip = new Label(ID_T_OPPORTUNISTIC);
opportunisticTooltip.add(new InfoTooltipBehavior());
editor.add(opportunisticTooltip);
Label correlationTooltip = new Label(ID_T_CORRELATION);
correlationTooltip.add(new InfoTooltipBehavior());
editor.add(correlationTooltip);
Label reactionTooltip = new Label(ID_T_REACTION);
reactionTooltip.add(new InfoTooltipBehavior());
editor.add(reactionTooltip);
}
private IModel<String> createObjectSyncTypeDisplayModel(final ObjectSynchronizationType syncObject){
return new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
StringBuilder sb = new StringBuilder();
if (syncObject != null) {
sb.append(syncObject.getName() != null ? syncObject.getName() + " " : "");
SchemaHandlingStep.addKindAndIntent(sb, syncObject.getKind(), syncObject.getIntent());
sb.append(" => ");
sb.append(getTypeDisplayName(ResourceTypeUtil.fillDefaultFocusType(syncObject.getFocusType())));
}
return sb.toString();
}
};
}
// TODO move to some utils
private static String getTypeDisplayName(@NotNull QName name) {
return StringUtils.removeEnd(name.getLocalPart(), "Type");
}
private void addDisableClassModifier(Component component){
component.add(new AttributeAppender("class", new AbstractReadOnlyModel<String>() {
@Override
public String getObject() {
if(!isAnySelected()){
return " disabled";
}
return null;
}
}));
}
private Component getSyncObjectTable(){
return get(ID_TABLE_ROWS);
}
private Component getNavigator(){
return get(ID_PAGING);
}
private Component getSyncObjectEditor(){
return get(ID_OBJECT_SYNC_EDITOR);
}
public Component getReactionList() {
return get(createComponentPath(ID_OBJECT_SYNC_EDITOR, ID_EDITOR_REACTION));
}
public Component getCorrelationList() {
return get(createComponentPath(ID_OBJECT_SYNC_EDITOR, ID_EDITOR_EDITOR_CORRELATION));
}
private Component getThirdRowContainer(){
return get(ID_THIRD_ROW_CONTAINER);
}
private void insertEmptyThirdRow(){
getThirdRowContainer().replaceWith(new WebMarkupContainer(ID_THIRD_ROW_CONTAINER));
}
private void conditionEditPerformed(AjaxRequestTarget target){
WebMarkupContainer newContainer = new SynchronizationExpressionEditor(ID_THIRD_ROW_CONTAINER,
new PropertyModel<ExpressionType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".condition"), parentPage) {
@Override
public String getLabel(){
return "SynchronizationExpressionEditor.label.condition";
}
};
getThirdRowContainer().replaceWith(newContainer);
resetSelections(target);
target.add(getThirdRowContainer(), get(ID_OBJECT_SYNC_EDITOR), getPageBase().getFeedbackPanel());
}
private void confirmationEditPerformed(AjaxRequestTarget target){
WebMarkupContainer newContainer = new SynchronizationExpressionEditor(ID_THIRD_ROW_CONTAINER,
new PropertyModel<ExpressionType>(syncDtoModel, ResourceSynchronizationDto.F_SELECTED + ".confirmation"), parentPage){
@Override
public String getLabel(){
return "SynchronizationExpressionEditor.label.confirmation";
}
};
getThirdRowContainer().replaceWith(newContainer);
resetSelections(target);
target.add(getThirdRowContainer(), get(ID_OBJECT_SYNC_EDITOR), getPageBase().getFeedbackPanel());
}
private void correlationEditPerformed(AjaxRequestTarget target, @NotNull ConditionalSearchFilterType condition) {
if (condition.getCondition() == null) {
condition.setCondition(new ExpressionType()); // removed at save
}
resetSelections(target);
syncDtoModel.getObject().setSelectedCorrelation(condition);
WebMarkupContainer newContainer = new ConditionalSearchFilterEditor(ID_THIRD_ROW_CONTAINER,
new NonEmptyWrapperModel<>(new Model<>(condition)), parentPage);
getThirdRowContainer().replaceWith(newContainer);
target.add(getThirdRowContainer(), get(ID_OBJECT_SYNC_EDITOR), getPageBase().getFeedbackPanel());
}
private void reactionEditPerformed(AjaxRequestTarget target, SynchronizationReactionType reaction){
WebMarkupContainer newContainer = new SynchronizationReactionEditor(ID_THIRD_ROW_CONTAINER, new Model<>(reaction), this, parentPage);
getThirdRowContainer().replaceWith(newContainer);
for (SynchronizationActionType action : reaction.getAction()) {
if (action.getRef() != null) {
warn(getString("SynchronizationStep.message.unsupportedActionFormat"));
break;
}
}
resetSelections(target);
syncDtoModel.getObject().setSelectedReaction(reaction);
target.add(getThirdRowContainer(), get(ID_OBJECT_SYNC_EDITOR), getPageBase().getFeedbackPanel());
}
@Override
public void applyState() {
parentPage.refreshIssues(null);
if (parentPage.isReadOnly() || !isComplete()) {
return;
}
savePerformed();
insertEmptyThirdRow();
resetSelections(null);
}
private void savePerformed() {
PrismObject<ResourceType> oldResource;
PrismObject<ResourceType> newResource = resourceModel.getObject();
Task task = getPageBase().createSimpleTask(OPERATION_SAVE_SYNC);
OperationResult result = task.getResult();
ModelService modelService = getPageBase().getModelService();
boolean saved = false;
removeEmptyContainers(newResource.asObjectable());
try {
oldResource = WebModelServiceUtils.loadObject(ResourceType.class, newResource.getOid(), getPageBase(), task, result);
if (oldResource != null) {
ObjectDelta<ResourceType> delta = parentPage.computeDiff(oldResource, newResource);
if (!delta.isEmpty()) {
parentPage.logDelta(delta);
Collection<ObjectDelta<? extends ObjectType>> deltas = WebComponentUtil.createDeltaCollection(delta);
modelService.executeChanges(deltas, null, getPageBase().createSimpleTask(OPERATION_SAVE_SYNC), result);
parentPage.resetModels();
syncDtoModel.reset();
saved = true;
}
}
} catch (CommonException|RuntimeException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't save resource synchronization.", e);
result.recordFatalError(getString("SynchronizationStep.message.cantSave", e));
} finally {
result.computeStatusIfUnknown();
setResult(result);
}
if (parentPage.showSaveResultInPage(saved, result)) {
getPageBase().showResult(result);
}
}
private void removeEmptyContainers(ResourceType resourceType) {
if (resourceType.getSynchronization() == null) {
return;
}
for (ObjectSynchronizationType objectSync : resourceType.getSynchronization().getObjectSynchronization()) {
objectSync.getObjectClass().removeIf(name -> name == null || StringUtils.isBlank(name.getLocalPart()));
if (objectSync.getCondition() != null && ExpressionUtil.isEmpty(objectSync.getCondition())) {
objectSync.setCondition(null);
}
if (objectSync.getConfirmation() != null && ExpressionUtil.isEmpty(objectSync.getConfirmation())) {
objectSync.setConfirmation(null);
}
for (ConditionalSearchFilterType correlationFilter : objectSync.getCorrelation()) {
if (correlationFilter.getCondition() != null && ExpressionUtil.isEmpty(correlationFilter.getCondition())) {
correlationFilter.setCondition(null);
}
}
}
}
private void editSyncObjectPerformed(AjaxRequestTarget target, ObjectSynchronizationType syncObject) {
boolean wasAnySelected = isAnySelected();
syncDtoModel.getObject().setSelected(syncObject);
insertEmptyThirdRow();
resetSelections(target);
if (wasAnySelected) {
target.add(getSyncObjectTable(), getNavigator(), getSyncObjectEditor(), getThirdRowContainer());
} else {
target.add(this);
}
}
private void deleteSyncObjectPerformed(AjaxRequestTarget target, ObjectSynchronizationType syncObject) {
if (isSelected(syncObject)) {
syncDtoModel.getObject().setSelected(null);
insertEmptyThirdRow();
resetSelections(target);
target.add(getThirdRowContainer());
}
ArrayList<ObjectSynchronizationType> list = (ArrayList<ObjectSynchronizationType>) syncDtoModel.getObject().getObjectSynchronizationList();
list.remove(syncObject);
if (list.isEmpty()) {
insertEmptyThirdRow();
resetSelections(target);
target.add(getThirdRowContainer());
}
target.add(getSyncObjectEditor(), getSyncObjectTable(), getNavigator());
parentPage.refreshIssues(target);
}
private boolean isSelected(ObjectSynchronizationType syncObject) {
return syncDtoModel.getObject().getSelected() == syncObject;
}
private void addSyncObjectPerformed(AjaxRequestTarget target){
ObjectSynchronizationType syncObject = new ObjectSynchronizationType();
syncObject.setEnabled(true);
//syncObject.setName(generateName(getString("SynchronizationStep.label.newObjectType")));
resourceModel.getObject().asObjectable().getSynchronization().getObjectSynchronization().add(syncObject);
editSyncObjectPerformed(target, syncObject);
parentPage.refreshIssues(target);
}
private class UpdateNamesBehaviour extends EmptyOnChangeAjaxFormUpdatingBehavior {
@Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(getSyncObjectTable(), getSyncObjectEditor().get(ID_EDITOR_LABEL));
parentPage.refreshIssues(target);
}
}
private void resetSelections(AjaxRequestTarget target) {
ResourceSynchronizationDto dto = syncDtoModel.getObject();
if (dto.getSelectedCorrelation() != null) {
dto.setSelectedCorrelation(null);
if (target != null) {
target.add(getCorrelationList());
}
}
if (dto.getSelectedReaction() != null) {
dto.setSelectedReaction(null);
if (target != null) {
target.add(getReactionList());
}
}
}
private String generateName(String prefix) {
List<String> existing = new ArrayList<>();
for (ObjectSynchronizationType sync : syncDtoModel.getObject().getObjectSynchronizationList()) {
CollectionUtils.addIgnoreNull(existing, sync.getName());
}
return SchemaHandlingStep.generateName(existing, prefix);
}
}