com.evolveum.midpoint.web.page.admin.users.PageUser.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.web.page.admin.users.PageUser.java

Source

/*
 * Copyright (c) 2010-2013 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;

import com.evolveum.midpoint.common.refinery.RefinedResourceSchema;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.*;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPathSegment;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.prism.schema.SchemaRegistry;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.ResultHandler;
import com.evolveum.midpoint.schema.RetrieveOption;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectResolver;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.exception.SystemException;
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.application.AuthorizationAction;
import com.evolveum.midpoint.web.application.PageDescriptor;
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.component.AjaxSubmitButton;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenu;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItemAction;
import com.evolveum.midpoint.web.component.util.PrismPropertyModel;
import com.evolveum.midpoint.web.page.admin.users.component.ExecuteChangeOptionsDto;
import com.evolveum.midpoint.web.page.admin.users.component.ExecuteChangeOptionsPanel;
import com.evolveum.midpoint.web.component.assignment.AssignmentEditorDto;
import com.evolveum.midpoint.web.component.assignment.AssignmentEditorDtoType;
import com.evolveum.midpoint.web.component.assignment.AssignmentEditorPanel;
import com.evolveum.midpoint.web.component.data.TablePanel;
import com.evolveum.midpoint.web.component.dialog.ConfirmationDialog;
import com.evolveum.midpoint.web.component.prism.*;
import com.evolveum.midpoint.web.component.util.LoadableModel;
import com.evolveum.midpoint.web.component.util.ObjectWrapperUtil;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.PageBase;
import com.evolveum.midpoint.web.page.admin.server.PageTasks;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDto;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDtoProvider;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDtoProviderOptions;
import com.evolveum.midpoint.web.page.admin.users.component.AssignablePopupContent;
import com.evolveum.midpoint.web.page.admin.users.component.ResourcesPopup;
import com.evolveum.midpoint.web.page.admin.users.dto.SimpleUserResourceProvider;
import com.evolveum.midpoint.web.page.admin.users.dto.UserAccountDto;
import com.evolveum.midpoint.web.page.admin.users.dto.UserDtoStatus;
import com.evolveum.midpoint.web.resource.img.ImgResources;
import com.evolveum.midpoint.web.security.MidPointApplication;
import com.evolveum.midpoint.web.util.OnePageParameterEncoder;
import com.evolveum.midpoint.web.util.WebMiscUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
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.image.Image;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.*;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.ByteArrayResource;
import org.apache.wicket.request.resource.ContextRelativeResource;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.util.string.StringValue;

import javax.xml.namespace.QName;

import java.io.Serializable;
import java.util.*;

/**
 * @author lazyman
 */
@PageDescriptor(url = "/admin/user", encoder = OnePageParameterEncoder.class, action = {
        @AuthorizationAction(actionUri = PageAdminUsers.AUTH_USERS_ALL, label = PageAdminUsers.AUTH_USERS_ALL_LABEL, description = PageAdminUsers.AUTH_USERS_ALL_DESCRIPTION),
        @AuthorizationAction(actionUri = AuthorizationConstants.NS_AUTHORIZATION
                + "#user", label = "PageUser.auth.user.label", description = "PageUser.auth.user.description") })
public class PageUser extends PageAdminUsers {

    public static final String PARAM_RETURN_PAGE = "returnPage";
    private static final String DOT_CLASS = PageUser.class.getName() + ".";
    private static final String OPERATION_LOAD_USER = DOT_CLASS + "loadUser";
    private static final String OPERATION_LOAD_ASSIGNMENTS = DOT_CLASS + "loadAssignments";
    private static final String OPERATION_LOAD_ASSIGNMENT = DOT_CLASS + "loadAssignment";
    private static final String OPERATION_SEND_TO_SUBMIT = DOT_CLASS + "sendToSubmit";
    private static final String OPERATION_MODIFY_ACCOUNT = DOT_CLASS + "modifyAccount";
    private static final String OPERATION_PREPARE_ACCOUNTS = DOT_CLASS + "getAccountsForSubmit";
    private static final String OPERATION_LOAD_ACCOUNTS = DOT_CLASS + "loadAccounts";
    private static final String OPERATION_LOAD_ACCOUNT = DOT_CLASS + "loadAccount";
    private static final String OPERATION_SAVE = DOT_CLASS + "save";
    private static final String OPERATION_SEARCH_RESOURCE = DOT_CLASS + "searchAccountResource";

    private static final String MODAL_ID_RESOURCE = "resourcePopup";
    private static final String MODAL_ID_ASSIGNABLE = "assignablePopup";
    private static final String MODAL_ID_CONFIRM_DELETE_ACCOUNT = "confirmDeleteAccountPopup";
    private static final String MODAL_ID_CONFIRM_DELETE_ASSIGNMENT = "confirmDeleteAssignmentPopup";

    private static final String ID_MAIN_FORM = "mainForm";
    private static final String ID_ASSIGNMENT_EDITOR = "assignmentEditor";
    private static final String ID_ASSIGNMENT_LIST = "assignmentList";
    private static final String ID_TASK_TABLE = "taskTable";
    private static final String ID_USER_FORM = "userForm";
    private static final String ID_ACCOUNTS_DELTAS = "accountsDeltas";
    private static final String ID_EXECUTE_OPTIONS = "executeOptions";
    private static final String ID_ACCOUNT_LIST = "accountList";
    private static final String ID_ACCOUNTS = "accounts";
    private static final String ID_ASSIGNMENTS = "assignments";
    private static final String ID_TASKS = "tasks";
    private static final String ID_ACCOUNT_MENU = "accountMenu";
    private static final String ID_ASSIGNMENT_MENU = "assignmentMenu";
    private static final String ID_ACCOUNT_CHECK_ALL = "accountCheckAll";
    private static final String ID_ASSIGNMENT_CHECK_ALL = "assignmentCheckAll";

    private static final String ID_SUMMARY_PANEL = "summaryPanel";
    private static final String ID_SUMMARY_NAME = "summaryName";
    private static final String ID_SUMMARY_FULL_NAME = "summaryFullName";
    private static final String ID_SUMMARY_GIVEN_NAME = "summaryGivenName";
    private static final String ID_SUMMARY_FAMILY_NAME = "summaryFamilyName";
    private static final String ID_SUMMARY_PHOTO = "summaryPhoto";

    private static final Trace LOGGER = TraceManager.getTrace(PageUser.class);
    private LoadableModel<ObjectWrapper> userModel;
    private LoadableModel<List<UserAccountDto>> accountsModel;
    private LoadableModel<List<AssignmentEditorDto>> assignmentsModel;
    private IModel<PrismObject<UserType>> summaryUser;

    private LoadableModel<ExecuteChangeOptionsDto> executeOptionsModel = new LoadableModel<ExecuteChangeOptionsDto>(
            false) {

        @Override
        protected ExecuteChangeOptionsDto load() {
            return new ExecuteChangeOptionsDto();
        }
    };

    public PageUser() {
        this(null);
    }

    public PageUser(final PrismObject<UserType> userToEdit) {
        userModel = new LoadableModel<ObjectWrapper>(false) {

            @Override
            protected ObjectWrapper load() {
                return loadUserWrapper(userToEdit);
            }
        };
        accountsModel = new LoadableModel<List<UserAccountDto>>(false) {

            @Override
            protected List<UserAccountDto> load() {
                return loadAccountWrappers();
            }
        };
        assignmentsModel = new LoadableModel<List<AssignmentEditorDto>>(false) {

            @Override
            protected List<AssignmentEditorDto> load() {
                return loadAssignments();
            }
        };

        initLayout();
    }

    @Override
    protected IModel<String> createPageTitleModel() {
        return new LoadableModel<String>() {

            @Override
            protected String load() {
                if (!isEditingUser()) {
                    return createStringResource("pageUser.title.newUser").getObject();
                }

                return createStringResource("pageUser.title.editUser").getObject();
            }
        };
    }

    @Override
    protected IModel<String> createPageSubTitleModel() {
        return new LoadableModel<String>() {

            @Override
            protected String load() {
                if (!isEditingUser()) {
                    return createStringResource("pageUser.subTitle.newUser").getObject();
                }

                String name = userModel.getObject().getObject().asObjectable().getName().getOrig();
                return createStringResource("pageUser.subTitle.edituser", name).getObject();
            }
        };
    }

    private ObjectWrapper loadUserWrapper(PrismObject<UserType> userToEdit) {
        OperationResult result = new OperationResult(OPERATION_LOAD_USER);
        PrismObject<UserType> user = null;
        try {
            if (!isEditingUser()) {
                if (userToEdit == null) {
                    UserType userType = new UserType();
                    getMidpointApplication().getPrismContext().adopt(userType);
                    user = userType.asPrismObject();
                } else {
                    user = userToEdit;
                }
            } else {
                Task task = createSimpleTask(OPERATION_LOAD_USER);

                StringValue userOid = getPageParameters().get(OnePageParameterEncoder.PARAMETER);
                Collection options = SelectorOptions.createCollection(UserType.F_JPEG_PHOTO,
                        GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE));

                user = getModelService().getObject(UserType.class, userOid.toString(), options, task, result);
            }

            result.recordSuccess();
        } catch (Exception ex) {
            result.recordFatalError("Couldn't get user.", ex);
            LoggingUtils.logException(LOGGER, "Couldn't load user", ex);
        }

        if (!result.isSuccess()) {
            showResultInSession(result);
        }

        if (user == null) {
            if (isEditingUser()) {
                getSession().error(getString("pageUser.message.cantEditUser"));
            } else {
                getSession().error(getString("pageUser.message.cantNewUser"));
            }
            throw new RestartResponseException(PageUsers.class);
        }

        ContainerStatus status = isEditingUser() ? ContainerStatus.MODIFYING : ContainerStatus.ADDING;
        ObjectWrapper wrapper = null;
        try {
            wrapper = ObjectWrapperUtil.createObjectWrapper("pageUser.userDetails", null, user, status, this);
        } catch (Exception ex) {
            result.recordFatalError("Couldn't get user.", ex);
            LoggingUtils.logException(LOGGER, "Couldn't load user", ex);
            wrapper = new ObjectWrapper("pageUser.userDetails", null, user, null, status);
        }
        //        ObjectWrapper wrapper = new ObjectWrapper("pageUser.userDetails", null, user, status);
        if (wrapper.getResult() != null && !WebMiscUtil.isSuccessOrHandledError(wrapper.getResult())) {
            showResultInSession(wrapper.getResult());
        }

        wrapper.setShowEmpty(!isEditingUser());
        return wrapper;
    }

    private void initLayout() {
        Form mainForm = new Form(ID_MAIN_FORM);
        mainForm.setMaxSize(MidPointApplication.USER_PHOTO_MAX_FILE_SIZE);
        mainForm.setMultiPart(true);
        add(mainForm);

        initSummaryInfo(mainForm);

        PrismObjectPanel userForm = new PrismObjectPanel(ID_USER_FORM, userModel,
                new PackageResourceReference(ImgResources.class, ImgResources.USER_PRISM), mainForm) {

            @Override
            protected IModel<String> createDescription(IModel<ObjectWrapper> model) {
                return createStringResource("pageUser.description");
            }
        };
        mainForm.add(userForm);

        WebMarkupContainer accounts = new WebMarkupContainer(ID_ACCOUNTS);
        accounts.setOutputMarkupId(true);
        mainForm.add(accounts);
        initAccounts(accounts);

        WebMarkupContainer assignments = new WebMarkupContainer(ID_ASSIGNMENTS);
        assignments.setOutputMarkupId(true);
        mainForm.add(assignments);
        initAssignments(assignments);

        WebMarkupContainer tasks = new WebMarkupContainer(ID_TASKS);
        tasks.setOutputMarkupId(true);
        mainForm.add(tasks);
        initTasks(tasks);

        initButtons(mainForm);

        initResourceModal();
        initAssignableModal();
        initConfirmationDialogs();
    }

    private String getLabelFromPolyString(PolyStringType poly) {
        if (poly == null || poly.getOrig() == null) {
            return "-";
        } else {
            return poly.getOrig();
        }
    }

    private void initSummaryInfo(Form mainForm) {

        WebMarkupContainer summaryContainer = new WebMarkupContainer(ID_SUMMARY_PANEL);
        summaryContainer.setOutputMarkupId(true);

        summaryContainer.add(new VisibleEnableBehaviour() {

            @Override
            public boolean isVisible() {
                if (getPageParameters().get(OnePageParameterEncoder.PARAMETER).isEmpty()) {
                    return false;
                } else {
                    return true;
                }
            }
        });

        mainForm.add(summaryContainer);

        summaryUser = new AbstractReadOnlyModel<PrismObject<UserType>>() {

            @Override
            public PrismObject<UserType> getObject() {
                ObjectWrapper user = userModel.getObject();
                return user.getObject();
            }
        };

        summaryContainer
                .add(new Label(ID_SUMMARY_NAME, new PrismPropertyModel<UserType>(summaryUser, UserType.F_NAME)));
        summaryContainer.add(new Label(ID_SUMMARY_FULL_NAME,
                new PrismPropertyModel<UserType>(summaryUser, UserType.F_FULL_NAME)));
        summaryContainer.add(new Label(ID_SUMMARY_GIVEN_NAME,
                new PrismPropertyModel<UserType>(summaryUser, UserType.F_GIVEN_NAME)));
        summaryContainer.add(new Label(ID_SUMMARY_FAMILY_NAME,
                new PrismPropertyModel<UserType>(summaryUser, UserType.F_FAMILY_NAME)));

        Image img = new Image(ID_SUMMARY_PHOTO, new AbstractReadOnlyModel<AbstractResource>() {

            @Override
            public AbstractResource getObject() {
                if (summaryUser.getObject().asObjectable().getJpegPhoto() != null) {
                    return new ByteArrayResource("image/jpeg",
                            summaryUser.getObject().asObjectable().getJpegPhoto());
                } else {
                    return new ContextRelativeResource("img/placeholder.png");
                }

            }
        });
        summaryContainer.add(img);
    }

    private void initConfirmationDialogs() {
        ConfirmationDialog dialog = new ConfirmationDialog(MODAL_ID_CONFIRM_DELETE_ACCOUNT,
                createStringResource("pageUser.title.confirmDelete"), new AbstractReadOnlyModel<String>() {

                    @Override
                    public String getObject() {
                        return createStringResource("pageUser.message.deleteAccountConfirm",
                                getSelectedAccounts().size()).getString();
                    }
                }) {

            @Override
            public void yesPerformed(AjaxRequestTarget target) {
                close(target);
                deleteAccountConfirmedPerformed(target, getSelectedAccounts());
            }
        };
        add(dialog);

        dialog = new ConfirmationDialog(MODAL_ID_CONFIRM_DELETE_ASSIGNMENT,
                createStringResource("pageUser.title.confirmDelete"), new AbstractReadOnlyModel<String>() {

                    @Override
                    public String getObject() {
                        return createStringResource("pageUser.message.deleteAssignmentConfirm",
                                getSelectedAssignments().size()).getString();
                    }
                }) {

            @Override
            public void yesPerformed(AjaxRequestTarget target) {
                close(target);
                deleteAssignmentConfirmedPerformed(target, getSelectedAssignments());
            }
        };
        add(dialog);

        // TODO: uncoment later -> check for unsaved changes
        // dialog = new ConfirmationDialog(MODAL_ID_CONFIRM_CANCEL,
        // createStringResource("pageUser.title.confirmCancel"), new
        // AbstractReadOnlyModel<String>() {
        //
        // @Override
        // public String getObject() {
        // return createStringResource("pageUser.message.cancelConfirm",
        // getSelectedAssignments().size()).getString();
        // }
        // }) {
        //
        // @Override
        // public void yesPerformed(AjaxRequestTarget target) {
        // close(target);
        // setResponsePage(PageUsers.class);
        // // deleteAssignmentConfirmedPerformed(target,
        // getSelectedAssignments());
        // }
        // };
        // add(dialog);
    }

    private List<InlineMenuItem> createAssignmentsMenu() {
        List<InlineMenuItem> items = new ArrayList<InlineMenuItem>();
        InlineMenuItem item = new InlineMenuItem(createStringResource("pageUser.menu.assignAccount"),
                new InlineMenuItemAction() {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        showAssignablePopup(target, ResourceType.class);
                    }
                });
        items.add(item);
        item = new InlineMenuItem(createStringResource("pageUser.menu.assignRole"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                showAssignablePopup(target, RoleType.class);
            }
        });
        items.add(item);
        item = new InlineMenuItem(createStringResource("pageUser.menu.assignOrg"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                showAssignablePopup(target, OrgType.class);
            }
        });
        items.add(item);
        items.add(new InlineMenuItem());
        item = new InlineMenuItem(createStringResource("pageUser.menu.unassign"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                deleteAssignmentPerformed(target);
            }
        });
        items.add(item);

        return items;
    }

    private List<InlineMenuItem> createAccountsMenu() {
        List<InlineMenuItem> items = new ArrayList<InlineMenuItem>();
        InlineMenuItem item = new InlineMenuItem(createStringResource("pageUser.button.addAccount"),
                new InlineMenuItemAction() {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        showModalWindow(MODAL_ID_RESOURCE, target);
                    }
                });
        items.add(item);
        items.add(new InlineMenuItem());
        item = new InlineMenuItem(createStringResource("pageUser.button.enable"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                updateAccountActivation(target, getSelectedAccounts(), true);
            }
        });
        items.add(item);
        item = new InlineMenuItem(createStringResource("pageUser.button.disable"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                updateAccountActivation(target, getSelectedAccounts(), false);
            }
        });
        items.add(item);
        item = new InlineMenuItem(createStringResource("pageUser.button.unlink"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                unlinkAccountPerformed(target, getSelectedAccounts());
            }
        });
        items.add(item);
        item = new InlineMenuItem(createStringResource("pageUser.button.unlock"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                unlockAccountPerformed(target, getSelectedAccounts());
            }
        });
        items.add(item);
        items.add(new InlineMenuItem());
        item = new InlineMenuItem(createStringResource("pageUser.button.delete"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                deleteAccountPerformed(target);
            }
        });
        items.add(item);

        return items;
    }

    private void initAccounts(final WebMarkupContainer accounts) {
        InlineMenu accountMenu = new InlineMenu(ID_ACCOUNT_MENU, new Model((Serializable) createAccountsMenu()));
        accounts.add(accountMenu);

        final ListView<UserAccountDto> accountList = new ListView<UserAccountDto>(ID_ACCOUNT_LIST, accountsModel) {

            @Override
            protected void populateItem(final ListItem<UserAccountDto> item) {
                PackageResourceReference packageRef;
                final UserAccountDto dto = item.getModelObject();

                Panel panel;

                if (dto.isLoadedOK()) {
                    packageRef = new PackageResourceReference(ImgResources.class, ImgResources.HDD_PRISM);

                    panel = new PrismObjectPanel("account",
                            new PropertyModel<ObjectWrapper>(item.getModel(), "object"), packageRef,
                            (Form) PageUser.this.get(ID_MAIN_FORM)) {

                        @Override
                        protected Component createHeader(String id, IModel<ObjectWrapper> model) {
                            return new CheckTableHeader(id, model) {

                                @Override
                                protected List<InlineMenuItem> createMenuItems() {
                                    return createDefaultMenuItems(getModel());
                                }
                            };
                        }
                    };
                } else {
                    panel = new SimpleErrorPanel("account", item.getModel()) {

                        @Override
                        public void onShowMorePerformed(AjaxRequestTarget target) {
                            OperationResult fetchResult = dto.getResult();
                            if (fetchResult != null) {
                                showResult(fetchResult);
                                target.add(getPageBase().getFeedbackPanel());
                            }
                        }
                    };
                }

                panel.setOutputMarkupId(true);
                item.add(panel);
            }
        };

        AjaxCheckBox accountCheckAll = new AjaxCheckBox(ID_ACCOUNT_CHECK_ALL, new Model()) {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                for (UserAccountDto dto : accountList.getModelObject()) {
                    if (dto.isLoadedOK()) {
                        ObjectWrapper accModel = dto.getObject();
                        accModel.setSelected(getModelObject());
                    }
                }

                target.add(accounts);
            }
        };
        accounts.add(accountCheckAll);

        accounts.add(accountList);
    }

    private List<UserAccountDto> loadAccountWrappers() {
        List<UserAccountDto> list = new ArrayList<UserAccountDto>();

        ObjectWrapper user = userModel.getObject();
        PrismObject<UserType> prismUser = user.getObject();
        List<ObjectReferenceType> references = prismUser.asObjectable().getLinkRef();

        Task task = createSimpleTask(OPERATION_LOAD_ACCOUNT);
        for (ObjectReferenceType reference : references) {
            OperationResult subResult = new OperationResult(OPERATION_LOAD_ACCOUNT);
            try {
                Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions
                        .createCollection(ShadowType.F_RESOURCE, GetOperationOptions.createResolve());

                if (reference.getOid() == null) {
                    continue;
                }

                PrismObject<ShadowType> account = getModelService().getObject(ShadowType.class, reference.getOid(),
                        options, task, subResult);
                ShadowType accountType = account.asObjectable();

                OperationResultType fetchResult = accountType.getFetchResult();

                ResourceType resource = accountType.getResource();
                String resourceName = WebMiscUtil.getName(resource);

                ObjectWrapper wrapper = ObjectWrapperUtil.createObjectWrapper(resourceName,
                        WebMiscUtil.getOrigStringFromPoly(accountType.getName()), account,
                        ContainerStatus.MODIFYING, this);
                //                ObjectWrapper wrapper = new ObjectWrapper(resourceName, WebMiscUtil.getOrigStringFromPoly(accountType
                //                        .getName()), account, ContainerStatus.MODIFYING);
                wrapper.setFetchResult(OperationResult.createOperationResult(fetchResult));
                wrapper.setSelectable(true);
                wrapper.setMinimalized(true);

                PrismContainer<ShadowAssociationType> associationContainer = account
                        .findContainer(ShadowType.F_ASSOCIATION);
                if (associationContainer != null && associationContainer.getValues() != null) {
                    List<PrismProperty> associations = new ArrayList<>(associationContainer.getValues().size());
                    for (PrismContainerValue associationVal : associationContainer.getValues()) {
                        ShadowAssociationType associationType = (ShadowAssociationType) associationVal
                                .asContainerable();
                        PrismObject<ShadowType> association = getModelService().getObject(ShadowType.class,
                                associationType.getShadowRef().getOid(), null, task, subResult);
                        associations.add(association.findProperty(ShadowType.F_NAME));

                    }

                    wrapper.setAssociations(associations);

                }

                list.add(new UserAccountDto(wrapper, UserDtoStatus.MODIFY));

                subResult.recomputeStatus();
            } catch (ObjectNotFoundException ex) {
                // this is fix for MID-854, full user/accounts/assignments reload if accountRef reference is broken
                // because consistency already fixed it.

                userModel.reset();
                accountsModel.reset();
                assignmentsModel.reset();
            } catch (Exception ex) {
                subResult.recordFatalError("Couldn't load account." + ex.getMessage(), ex);
                LoggingUtils.logException(LOGGER, "Couldn't load account", ex);
                list.add(new UserAccountDto(false, getResourceName(reference.getOid()), subResult));
            } finally {
                subResult.computeStatus();
            }
        }

        return list;
    }

    private String getResourceName(String oid) {
        OperationResult result = new OperationResult(OPERATION_SEARCH_RESOURCE);
        Task task = createSimpleTask(OPERATION_SEARCH_RESOURCE);

        try {
            Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions
                    .createCollection(GetOperationOptions.createRaw());

            PrismObject<ShadowType> shadow = getModelService().getObject(ShadowType.class, oid, options, task,
                    result);
            PrismObject<ResourceType> resource = getModelService().getObject(ResourceType.class,
                    shadow.asObjectable().getResourceRef().getOid(), null, task, result);

            if (resource != null) {
                return WebMiscUtil.getOrigStringFromPoly(resource.asObjectable().getName());
            }
        } catch (Exception e) {
            result.recordFatalError("Account Resource was not found. " + e.getMessage());
            LoggingUtils.logException(LOGGER, "Account Resource was not found.", e);
            showResult(result);
        }

        return "-";
    }

    private List<AssignmentEditorDto> loadAssignments() {
        List<AssignmentEditorDto> list = new ArrayList<AssignmentEditorDto>();

        OperationResult result = new OperationResult(OPERATION_LOAD_ASSIGNMENTS);

        ObjectWrapper user = userModel.getObject();
        PrismObject<UserType> prismUser = user.getObject();
        List<AssignmentType> assignments = prismUser.asObjectable().getAssignment();
        for (AssignmentType assignment : assignments) {
            ObjectType targetObject = null;
            AssignmentEditorDtoType type = AssignmentEditorDtoType.ACCOUNT_CONSTRUCTION;
            if (assignment.getTarget() != null) {
                // object assignment
                targetObject = assignment.getTarget();
                type = AssignmentEditorDtoType.getType(targetObject.getClass());
            } else if (assignment.getTargetRef() != null) {
                // object assignment through reference
                ObjectReferenceType ref = assignment.getTargetRef();
                PrismObject target = getReference(ref, result);

                if (target != null) {
                    targetObject = (ObjectType) target.asObjectable();
                    type = AssignmentEditorDtoType.getType(target.getCompileTimeClass());
                }
            } else if (assignment.getConstruction() != null) {
                // account assignment through account construction
                ConstructionType construction = assignment.getConstruction();
                if (construction.getResource() != null) {
                    targetObject = construction.getResource();
                } else if (construction.getResourceRef() != null) {
                    ObjectReferenceType ref = construction.getResourceRef();
                    PrismObject target = getReference(ref, result);
                    if (target != null) {
                        targetObject = (ObjectType) target.asObjectable();
                    }
                }
            }

            list.add(new AssignmentEditorDto(targetObject, type, UserDtoStatus.MODIFY, assignment));
        }

        Collections.sort(list);

        return list;
    }

    private PrismObject getReference(ObjectReferenceType ref, OperationResult result) {
        OperationResult subResult = result.createSubresult(OPERATION_LOAD_ASSIGNMENT);
        subResult.addParam("targetRef", ref.getOid());
        PrismObject target = null;
        try {
            Task task = createSimpleTask(OPERATION_LOAD_ASSIGNMENT);
            Class type = ObjectType.class;
            if (ref.getType() != null) {
                type = getPrismContext().getSchemaRegistry().determineCompileTimeClass(ref.getType());
            }
            target = getModelService().getObject(type, ref.getOid(), null, task, subResult);
            subResult.recordSuccess();
        } catch (Exception ex) {
            LoggingUtils.logException(LOGGER, "Couldn't get assignment target ref", ex);
            subResult.recordFatalError("Couldn't get assignment target ref.", ex);
        }

        return target;
    }

    private void initAssignments(final WebMarkupContainer assignments) {
        InlineMenu accountMenu = new InlineMenu(ID_ASSIGNMENT_MENU,
                new Model((Serializable) createAssignmentsMenu()));
        assignments.add(accountMenu);

        final ListView<AssignmentEditorDto> assignmentList = new ListView<AssignmentEditorDto>(ID_ASSIGNMENT_LIST,
                assignmentsModel) {

            @Override
            protected void populateItem(final ListItem<AssignmentEditorDto> item) {
                AssignmentEditorPanel assignmentEditor = new AssignmentEditorPanel(ID_ASSIGNMENT_EDITOR,
                        item.getModel());
                item.add(assignmentEditor);
            }
        };
        assignmentList.setOutputMarkupId(true);
        assignments.add(assignmentList);

        AjaxCheckBox assignmentCheckAll = new AjaxCheckBox(ID_ASSIGNMENT_CHECK_ALL, new Model()) {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                for (AssignmentEditorDto item : assignmentList.getModelObject()) {
                    item.setSelected(this.getModelObject());
                }

                target.add(assignments);
            }
        };
        assignments.add(assignmentCheckAll);
    }

    private void initTasks(WebMarkupContainer tasks) {
        List<IColumn<TaskDto, String>> taskColumns = initTaskColumns();
        final TaskDtoProvider taskDtoProvider = new TaskDtoProvider(PageUser.this,
                TaskDtoProviderOptions.minimalOptions());
        taskDtoProvider.setQuery(createTaskQuery(null));
        TablePanel taskTable = new TablePanel<TaskDto>(ID_TASK_TABLE, taskDtoProvider, taskColumns) {

            @Override
            protected void onInitialize() {
                super.onInitialize();
                StringValue oidValue = getPageParameters().get(OnePageParameterEncoder.PARAMETER);

                taskDtoProvider.setQuery(createTaskQuery(oidValue != null ? oidValue.toString() : null));
            }
        };
        tasks.add(taskTable);

        tasks.add(new VisibleEnableBehaviour() {
            @Override
            public boolean isVisible() {
                return taskDtoProvider.size() > 0;
            }
        });
    }

    private ObjectQuery createTaskQuery(String oid) {
        List<ObjectFilter> filters = new ArrayList<ObjectFilter>();

        if (oid == null) {
            oid = "non-existent"; // TODO !!!!!!!!!!!!!!!!!!!!
        }
        try {
            filters.add(
                    RefFilter.createReferenceEqual(TaskType.F_OBJECT_REF, TaskType.class, getPrismContext(), oid));
            filters.add(NotFilter.createNot(EqualFilter.createEqual(TaskType.F_EXECUTION_STATUS, TaskType.class,
                    getPrismContext(), null, TaskExecutionStatusType.CLOSED)));
            filters.add(EqualFilter.createEqual(TaskType.F_PARENT, TaskType.class, getPrismContext(), null));
        } catch (SchemaException e) {
            throw new SystemException("Unexpected SchemaException when creating task filter", e);
        }

        return new ObjectQuery().createObjectQuery(AndFilter.createAnd(filters));
    }

    private List<IColumn<TaskDto, String>> initTaskColumns() {
        List<IColumn<TaskDto, String>> columns = new ArrayList<IColumn<TaskDto, String>>();

        columns.add(PageTasks.createTaskNameColumn(this, "pageUser.task.name"));
        columns.add(PageTasks.createTaskCategoryColumn(this, "pageUser.task.category"));
        columns.add(PageTasks.createTaskExecutionStatusColumn(this, "pageUser.task.execution"));
        columns.add(PageTasks.createTaskResultStatusColumn(this, "pageUser.task.status"));
        return columns;
    }

    private void initButtons(Form mainForm) {
        AjaxSubmitButton save = new AjaxSubmitButton("save", createStringResource("pageUser.button.save")) {

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                savePerformed(target);
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(getFeedbackPanel());
            }
        };
        mainForm.add(save);

        AjaxButton back = new AjaxButton("back", createStringResource("pageUser.button.back")) {

            @Override
            public void onClick(AjaxRequestTarget target) {
                cancelPerformed(target);
            }
        };
        mainForm.add(back);

        mainForm.add(new ExecuteChangeOptionsPanel(ID_EXECUTE_OPTIONS, executeOptionsModel, true));
    }

    private void showAssignablePopup(AjaxRequestTarget target, Class<? extends ObjectType> type) {
        ModalWindow modal = (ModalWindow) get(MODAL_ID_ASSIGNABLE);
        AssignablePopupContent content = (AssignablePopupContent) modal.get(modal.getContentId());
        content.setType(type);
        showModalWindow(MODAL_ID_ASSIGNABLE, target);
    }

    private void initResourceModal() {
        ModalWindow window = createModalWindow(MODAL_ID_RESOURCE,
                createStringResource("pageUser.title.selectResource"), 1100, 520);

        SimpleUserResourceProvider provider = new SimpleUserResourceProvider(this, accountsModel);
        window.setContent(new ResourcesPopup(window.getContentId(), provider) {

            @Override
            protected void addPerformed(AjaxRequestTarget target, List<ResourceType> newResources) {
                addSelectedAccountPerformed(target, newResources);
            }
        });

        add(window);
    }

    private void initAssignableModal() {
        ModalWindow window = createModalWindow(MODAL_ID_ASSIGNABLE,
                createStringResource("pageUser.title.selectAssignable"), 1100, 520);
        window.setContent(new AssignablePopupContent(window.getContentId()) {

            @Override
            protected void addPerformed(AjaxRequestTarget target, List<ObjectType> selected) {
                addSelectedAssignablePerformed(target, selected);
            }
        });
        add(window);
    }

    private boolean isEditingUser() {
        StringValue userOid = getPageParameters().get(OnePageParameterEncoder.PARAMETER);
        return userOid != null && StringUtils.isNotEmpty(userOid.toString());
    }

    private void cancelPerformed(AjaxRequestTarget target) {
        // uncoment later -> check for changes to not allow leave the page when
        // some changes were made
        // try{
        // if (userModel.getObject().getOldDelta() != null &&
        // !userModel.getObject().getOldDelta().isEmpty() ||
        // userModel.getObject().getObjectDelta() != null &&
        // !userModel.getObject().getObjectDelta().isEmpty()){
        // showModalWindow(MODAL_ID_CONFIRM_CANCEL, target);
        // } else{
        StringValue orgReturn = getPageParameters().get(PARAM_RETURN_PAGE);
        if (PageOrgTree.PARAM_ORG_RETURN.equals(orgReturn.toString())) {
            setResponsePage(PageOrgTree.class);
        } else {
            setResponsePage(PageUsers.class);
        }

        // }
        // }catch(Exception ex){
        // LoggingUtils.logException(LOGGER, "Could not return to user list",
        // ex);
        // }
    }

    private List<ObjectDelta<? extends ObjectType>> modifyAccounts(OperationResult result) {
        List<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>();

        List<UserAccountDto> accounts = accountsModel.getObject();
        OperationResult subResult = null;
        for (UserAccountDto account : accounts) {
            if (!account.isLoadedOK())
                continue;

            try {
                ObjectWrapper accountWrapper = account.getObject();
                ObjectDelta delta = accountWrapper.getObjectDelta();
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("Account delta computed from form:\n{}", new Object[] { delta.debugDump(3) });
                }

                if (!UserDtoStatus.MODIFY.equals(account.getStatus())) {
                    continue;
                }

                if (delta.isEmpty()
                        && (accountWrapper.getOldDelta() == null || accountWrapper.getOldDelta().isEmpty())) {
                    continue;
                }

                if (accountWrapper.getOldDelta() != null) {
                    delta = ObjectDelta.summarize(delta, accountWrapper.getOldDelta());
                }

                //what is this???
                //            subResult = result.createSubresult(OPERATION_MODIFY_ACCOUNT);

                WebMiscUtil.encryptCredentials(delta, true, getMidpointApplication());
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("Modifying account:\n{}", new Object[] { delta.debugDump(3) });
                }

                deltas.add(delta);
                //            subResult.recordSuccess();
            } catch (Exception ex) {
                //            if (subResult != null) {
                result.recordFatalError("Couldn't compute account delta.", ex);
                //            }
                LoggingUtils.logException(LOGGER, "Couldn't compute account delta", ex);
            }
        }

        return deltas;
    }

    private ArrayList<PrismObject> getAccountsForSubmit(OperationResult result,
            Collection<ObjectDelta<? extends ObjectType>> deltas) {
        List<UserAccountDto> accounts = accountsModel.getObject();
        ArrayList<PrismObject> prismAccounts = new ArrayList<PrismObject>();
        OperationResult subResult = null;
        Task task = createSimpleTask(OPERATION_PREPARE_ACCOUNTS);
        for (UserAccountDto account : accounts) {
            prismAccounts.add(account.getObject().getObject());
            try {
                ObjectWrapper accountWrapper = account.getObject();
                ObjectDelta delta = accountWrapper.getObjectDelta();
                if (delta.isEmpty()) {
                    if (accountWrapper.getOldDelta() == null || accountWrapper.getOldDelta().isEmpty()) {
                        continue;
                    } else {
                        delta = accountWrapper.getOldDelta();
                    }
                } else {
                    if (accountWrapper.getOldDelta() != null && !accountWrapper.getOldDelta().isEmpty()) {
                        delta = ObjectDelta.summarize(delta, accountWrapper.getOldDelta());

                    }
                }

                WebMiscUtil.encryptCredentials(delta, true, getMidpointApplication());
                subResult = result.createSubresult(OPERATION_PREPARE_ACCOUNTS);
                deltas.add(delta);
                subResult.recordSuccess();
            } catch (Exception ex) {
                if (subResult != null) {
                    subResult.recordFatalError("Preparing account failed.", ex);
                }
                LoggingUtils.logException(LOGGER, "Couldn't prepare account for submit", ex);
            }
        }
        return prismAccounts;
    }

    private void prepareUserForAdd(PrismObject<UserType> user) throws SchemaException {
        UserType userType = user.asObjectable();
        // handle added accounts
        List<UserAccountDto> accounts = accountsModel.getObject();
        for (UserAccountDto accDto : accounts) {
            if (!accDto.isLoadedOK()) {
                continue;
            }

            if (!UserDtoStatus.ADD.equals(accDto.getStatus())) {
                warn(getString("pageUser.message.illegalAccountState", accDto.getStatus()));
                continue;
            }

            ObjectWrapper accountWrapper = accDto.getObject();
            ObjectDelta delta = accountWrapper.getObjectDelta();
            PrismObject<ShadowType> account = delta.getObjectToAdd();
            WebMiscUtil.encryptCredentials(account, true, getMidpointApplication());

            userType.getLink().add(account.asObjectable());
        }

        PrismObjectDefinition userDef = user.getDefinition();
        PrismContainerDefinition assignmentDef = userDef.findContainerDefinition(UserType.F_ASSIGNMENT);

        // handle added assignments
        List<AssignmentEditorDto> assignments = assignmentsModel.getObject();
        for (AssignmentEditorDto assDto : assignments) {
            if (!UserDtoStatus.ADD.equals(assDto.getStatus())) {
                warn(getString("pageUser.message.illegalAssignmentState", assDto.getStatus()));
                continue;
            }

            AssignmentType assignment = new AssignmentType();
            PrismContainerValue value = assDto.getNewValue();
            assignment.setupContainerValue(value);
            value.applyDefinition(assignmentDef, false);
            userType.getAssignment().add(assignment.clone());

            // todo remove this block [lazyman] after model is updated - it has
            // to remove resource from accountConstruction
            removeResourceFromAccConstruction(assignment);
        }
    }

    /**
     * remove this method after model is updated - it has to remove resource
     * from accountConstruction
     */
    @Deprecated
    private void removeResourceFromAccConstruction(AssignmentType assignment) {
        ConstructionType accConstruction = assignment.getConstruction();
        if (accConstruction == null || accConstruction.getResource() == null) {
            return;
        }

        ObjectReferenceType ref = new ObjectReferenceType();
        ref.setOid(assignment.getConstruction().getResource().getOid());
        ref.setType(ResourceType.COMPLEX_TYPE);
        assignment.getConstruction().setResourceRef(ref);
        assignment.getConstruction().setResource(null);
    }

    private ReferenceDelta prepareUserAccountsDeltaForModify(PrismReferenceDefinition refDef)
            throws SchemaException {
        ReferenceDelta refDelta = new ReferenceDelta(refDef);

        List<UserAccountDto> accounts = accountsModel.getObject();
        for (UserAccountDto accDto : accounts) {
            if (accDto.isLoadedOK()) {
                ObjectWrapper accountWrapper = accDto.getObject();
                ObjectDelta delta = accountWrapper.getObjectDelta();
                PrismReferenceValue refValue = new PrismReferenceValue(null, OriginType.USER_ACTION, null);

                PrismObject<ShadowType> account;
                switch (accDto.getStatus()) {
                case ADD:
                    account = delta.getObjectToAdd();
                    WebMiscUtil.encryptCredentials(account, true, getMidpointApplication());
                    refValue.setObject(account);
                    refDelta.addValueToAdd(refValue);
                    break;
                case DELETE:
                    account = accountWrapper.getObject();
                    refValue.setObject(account);
                    refDelta.addValueToDelete(refValue);
                    break;
                case MODIFY:
                    // nothing to do, account modifications were applied before
                    continue;
                case UNLINK:
                    refValue.setOid(delta.getOid());
                    refValue.setTargetType(ShadowType.COMPLEX_TYPE);
                    refDelta.addValueToDelete(refValue);
                    break;
                default:
                    warn(getString("pageUser.message.illegalAccountState", accDto.getStatus()));
                }
            }
        }

        return refDelta;
    }

    private ContainerDelta handleAssignmentDeltas(ObjectDelta<UserType> userDelta, PrismContainerDefinition def)
            throws SchemaException {
        ContainerDelta assDelta = new ContainerDelta(new ItemPath(), UserType.F_ASSIGNMENT, def);

        PrismObject<UserType> user = userModel.getObject().getObject();
        PrismObjectDefinition userDef = user.getDefinition();
        PrismContainerDefinition assignmentDef = userDef.findContainerDefinition(UserType.F_ASSIGNMENT);

        List<AssignmentEditorDto> assignments = assignmentsModel.getObject();
        for (AssignmentEditorDto assDto : assignments) {
            PrismContainerValue newValue = assDto.getNewValue();
            switch (assDto.getStatus()) {
            case ADD:
                newValue.applyDefinition(assignmentDef, false);
                assDelta.addValueToAdd(newValue.clone());
                break;
            case DELETE:
                PrismContainerValue oldValue = assDto.getOldValue();
                oldValue.applyDefinition(assignmentDef);
                assDelta.addValueToDelete(oldValue.clone());
                break;
            case MODIFY:
                if (!assDto.isModified()) {
                    LOGGER.trace("Assignment '{}' not modified.", new Object[] { assDto.getName() });
                    continue;
                }

                handleModifyAssignmentDelta(assDto, assignmentDef, newValue, userDelta);
                break;
            default:
                warn(getString("pageUser.message.illegalAssignmentState", assDto.getStatus()));
            }
        }

        if (!assDelta.isEmpty()) {
            userDelta.addModification(assDelta);
        }

        // todo remove this block [lazyman] after model is updated - it has to
        // remove resource from accountConstruction
        Collection<PrismContainerValue> values = assDelta.getValues(PrismContainerValue.class);
        for (PrismContainerValue value : values) {
            AssignmentType ass = new AssignmentType();
            ass.setupContainerValue(value);
            removeResourceFromAccConstruction(ass);
        }

        return assDelta;
    }

    private void handleModifyAssignmentDelta(AssignmentEditorDto assDto, PrismContainerDefinition assignmentDef,
            PrismContainerValue newValue, ObjectDelta<UserType> userDelta) throws SchemaException {
        LOGGER.debug("Handling modified assignment '{}', computing delta.", new Object[] { assDto.getName() });

        PrismValue oldValue = assDto.getOldValue();
        Collection<? extends ItemDelta> deltas = oldValue.diff(newValue);

        for (ItemDelta delta : deltas) {
            ItemPath deltaPath = delta.getPath().rest();
            ItemDefinition deltaDef = assignmentDef.findItemDefinition(deltaPath);

            delta.setParentPath(joinPath(oldValue.getPath(), delta.getPath().allExceptLast()));
            delta.applyDefinition(deltaDef);

            userDelta.addModification(delta);
        }
    }

    private ItemPath joinPath(ItemPath path, ItemPath deltaPath) {
        List<ItemPathSegment> newPath = new ArrayList<ItemPathSegment>();

        ItemPathSegment firstDeltaSegment = deltaPath != null ? deltaPath.first() : null;
        if (path != null) {
            for (ItemPathSegment seg : path.getSegments()) {
                if (seg.equals(firstDeltaSegment)) {
                    break;
                }
                newPath.add(seg);
            }
        }
        if (deltaPath != null) {
            newPath.addAll(deltaPath.getSegments());
        }

        return new ItemPath(newPath);
    }

    private void prepareUserDeltaForModify(ObjectDelta<UserType> userDelta) throws SchemaException {
        // handle accounts
        SchemaRegistry registry = getPrismContext().getSchemaRegistry();
        PrismObjectDefinition objectDefinition = registry.findObjectDefinitionByCompileTimeClass(UserType.class);
        PrismReferenceDefinition refDef = objectDefinition.findReferenceDefinition(UserType.F_LINK_REF);
        ReferenceDelta refDelta = prepareUserAccountsDeltaForModify(refDef);
        if (!refDelta.isEmpty()) {
            userDelta.addModification(refDelta);
        }

        // handle assignments
        PrismContainerDefinition def = objectDefinition.findContainerDefinition(UserType.F_ASSIGNMENT);
        handleAssignmentDeltas(userDelta, def);
    }

    private void savePerformed(AjaxRequestTarget target) {
        LOGGER.debug("Save user.");

        OperationResult result = new OperationResult(OPERATION_SAVE);
        ObjectWrapper userWrapper = userModel.getObject();
        // todo: improve, delta variable is quickfix for MID-1006
        // redirecting to user list page everytime user is created in repository
        // during user add in gui,
        // and we're not taking care about account/assignment create errors
        // (error message is still displayed)
        ObjectDelta delta;

        Task task = createSimpleTask(OPERATION_SEND_TO_SUBMIT);
        ExecuteChangeOptionsDto executeOptions = executeOptionsModel.getObject();
        ModelExecuteOptions options = executeOptions.createOptions();
        LOGGER.debug("Using options {}.", new Object[] { executeOptions });
        // try {

        try {
            delta = userWrapper.getObjectDelta();
            if (userWrapper.getOldDelta() != null) {
                delta = ObjectDelta.summarize(userWrapper.getOldDelta(), delta);
            }
            delta.setPrismContext(getPrismContext());
            if (LOGGER.isTraceEnabled()) {
                LOGGER.trace("User delta computed from form:\n{}", new Object[] { delta.debugDump(3) });
            }
        } catch (Exception ex) {
            result.recordFatalError(getString("pageUser.message.cantCreateUser"), ex);
            LoggingUtils.logException(LOGGER, "Create user failed", ex);
            showResult(result);
            return;
        }

        switch (userWrapper.getStatus()) {
        case ADDING:
            try {
                PrismObject<UserType> user = delta.getObjectToAdd();
                WebMiscUtil.encryptCredentials(user, true, getMidpointApplication());
                prepareUserForAdd(user);
                getPrismContext().adopt(user, UserType.class);
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("Delta before add user:\n{}", new Object[] { delta.debugDump(3) });
                }

                if (!delta.isEmpty()) {
                    getModelService().executeChanges(WebMiscUtil.createDeltaCollection(delta), options, task,
                            result);
                } else {
                    result.recordSuccess();
                }
            } catch (Exception ex) {
                result.recordFatalError(getString("pageUser.message.cantCreateUser"), ex);
                LoggingUtils.logException(LOGGER, "Create user failed", ex);
            }
            break;

        case MODIFYING:
            try {
                WebMiscUtil.encryptCredentials(delta, true, getMidpointApplication());
                prepareUserDeltaForModify(delta);

                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("Delta before modify user:\n{}", new Object[] { delta.debugDump(3) });
                }

                List<ObjectDelta<? extends ObjectType>> accountDeltas = modifyAccounts(result);
                Collection<ObjectDelta<? extends ObjectType>> deltas = new ArrayList<ObjectDelta<? extends ObjectType>>();

                if (!delta.isEmpty()) {
                    deltas.add(delta);
                }
                for (ObjectDelta accDelta : accountDeltas) {
                    if (!accDelta.isEmpty()) {
                        accDelta.setPrismContext(getPrismContext());
                        deltas.add(accDelta);
                    }
                }

                if (delta.isEmpty() && ModelExecuteOptions.isReconcile(options)) {
                    ObjectDelta emptyDelta = ObjectDelta.createEmptyModifyDelta(UserType.class,
                            userWrapper.getObject().getOid(), getPrismContext());
                    deltas.add(emptyDelta);
                    getModelService().executeChanges(deltas, options, task, result);
                } else if (!deltas.isEmpty()) {
                    getModelService().executeChanges(deltas, options, task, result);
                } else {
                    result.recordSuccess();
                }

            } catch (Exception ex) {
                if (!executeForceDelete(userWrapper, task, options, result)) {
                    result.recordFatalError(getString("pageUser.message.cantUpdateUser"), ex);
                    LoggingUtils.logException(LOGGER, getString("pageUser.message.cantUpdateUser"), ex);
                } else {
                    result.recomputeStatus();
                }
            }
            break;
        // support for add/delete containers (e.g. delete credentials)
        default:
            error(getString("pageUser.message.unsupportedState", userWrapper.getStatus()));
        }

        result.recomputeStatus();
        // } catch (Exception ex) {
        // if (!executeForceDelete(userWrapper, task, options, result)) {
        // result.recordFatalError(getString("pageUser.message.cantCreateUser"),
        // ex);
        // LoggingUtils.logException(LOGGER,
        // getString("pageUser.message.cantCreateUser"), ex);
        // } else{
        // result.recomputeStatus();
        // }
        // }

        boolean userAdded = delta != null && delta.isAdd() && StringUtils.isNotEmpty(delta.getOid());
        if (userAdded || !result.isFatalError()) {
            showResultInSession(result);
            // todo refactor this...what is this for? why it's using some
            // "shadow" param from result???
            PrismObject<UserType> user = userWrapper.getObject();
            UserType userType = user.asObjectable();
            for (ObjectReferenceType ref : userType.getLinkRef()) {
                Object o = findParam("shadow", ref.getOid(), result);
                if (o != null && o instanceof ShadowType) {
                    ShadowType accountType = (ShadowType) o;
                    OperationResultType fetchResult = accountType.getFetchResult();
                    if (fetchResult != null && !OperationResultStatusType.SUCCESS.equals(fetchResult.getStatus())) {
                        showResultInSession(OperationResult.createOperationResult(fetchResult));
                    }
                }
            }
            StringValue returnPage = getPageParameters().get(PARAM_RETURN_PAGE);
            if (!StringUtils.isBlank(returnPage.toString())
                    && PageOrgTree.PARAM_ORG_RETURN.equals(returnPage.toString())) {
                setResponsePage(PageOrgTree.class);
            } else {
                setResponsePage(PageUsers.class);
            }
        } else {
            showResult(result);
            target.add(getFeedbackPanel());
        }

    }

    private boolean executeForceDelete(ObjectWrapper userWrapper, Task task, ModelExecuteOptions options,
            OperationResult parentResult) {
        if (executeOptionsModel.getObject().isForce()) {
            OperationResult result = parentResult.createSubresult("Force delete operation");
            // List<UserAccountDto> accountDtos = accountsModel.getObject();
            // List<ReferenceDelta> refDeltas = new ArrayList<ReferenceDelta>();
            // ObjectDelta<UserType> forceDeleteDelta = null;
            // for (UserAccountDto accDto : accountDtos) {
            // if (accDto.getStatus() == UserDtoStatus.DELETE) {
            // ObjectWrapper accWrapper = accDto.getObject();
            // ReferenceDelta refDelta =
            // ReferenceDelta.createModificationDelete(UserType.F_ACCOUNT_REF,
            // userWrapper.getObject().getDefinition(), accWrapper.getObject());
            // refDeltas.add(refDelta);
            // }
            // }
            // if (!refDeltas.isEmpty()) {
            // forceDeleteDelta =
            // ObjectDelta.createModifyDelta(userWrapper.getObject().getOid(),
            // refDeltas,
            // UserType.class, getPrismContext());
            // }
            // PrismContainerDefinition def =
            // userWrapper.getObject().findContainer(UserType.F_ASSIGNMENT).getDefinition();
            // if (forceDeleteDelta == null) {
            // forceDeleteDelta =
            // ObjectDelta.createEmptyModifyDelta(UserType.class,
            // userWrapper.getObject().getOid(),
            // getPrismContext());
            // }
            try {
                ObjectDelta<UserType> forceDeleteDelta = getChange(userWrapper);
                forceDeleteDelta.setPrismContext(getPrismContext());

                if (forceDeleteDelta != null && !forceDeleteDelta.isEmpty()) {
                    getModelService().executeChanges(WebMiscUtil.createDeltaCollection(forceDeleteDelta), options,
                            task, result);
                }
            } catch (Exception ex) {
                result.recordFatalError("Failed to execute delete operation with force.");
                LoggingUtils.logException(LOGGER, "Failed to execute delete operation with force", ex);
                return false;
            }

            result.recomputeStatus();
            result.recordSuccessIfUnknown();
            return true;
        }
        return false;
    }

    private ObjectDelta getChange(ObjectWrapper userWrapper) throws SchemaException {

        List<UserAccountDto> accountDtos = accountsModel.getObject();
        List<ReferenceDelta> refDeltas = new ArrayList<ReferenceDelta>();
        ObjectDelta<UserType> forceDeleteDelta = null;
        for (UserAccountDto accDto : accountDtos) {
            if (!accDto.isLoadedOK()) {
                continue;
            }

            if (accDto.getStatus() == UserDtoStatus.DELETE) {
                ObjectWrapper accWrapper = accDto.getObject();
                ReferenceDelta refDelta = ReferenceDelta.createModificationDelete(UserType.F_LINK_REF,
                        userWrapper.getObject().getDefinition(), accWrapper.getObject());
                refDeltas.add(refDelta);
            } else if (accDto.getStatus() == UserDtoStatus.UNLINK) {
                ObjectWrapper accWrapper = accDto.getObject();
                ReferenceDelta refDelta = ReferenceDelta.createModificationDelete(UserType.F_LINK_REF,
                        userWrapper.getObject().getDefinition(), accWrapper.getObject().getOid());
                refDeltas.add(refDelta);
            }
        }
        if (!refDeltas.isEmpty()) {
            forceDeleteDelta = ObjectDelta.createModifyDelta(userWrapper.getObject().getOid(), refDeltas,
                    UserType.class, getPrismContext());
        }
        PrismContainerDefinition def = userWrapper.getObject().findContainer(UserType.F_ASSIGNMENT).getDefinition();
        if (forceDeleteDelta == null) {
            forceDeleteDelta = ObjectDelta.createEmptyModifyDelta(UserType.class, userWrapper.getObject().getOid(),
                    getPrismContext());
        }

        handleAssignmentDeltas(forceDeleteDelta, def);
        return forceDeleteDelta;
    }

    private Object object;

    public Object findParam(String param, String oid, OperationResult result) {

        for (OperationResult subResult : result.getSubresults()) {
            if (subResult != null && subResult.getParams() != null) {
                if (subResult.getParams().get(param) != null
                        && subResult.getParams().get(OperationResult.PARAM_OID) != null
                        && subResult.getParams().get(OperationResult.PARAM_OID).equals(oid)) {
                    return subResult.getParams().get(param);
                }
                object = findParam(param, oid, subResult);

            }
        }
        return object;
    }

    private List<UserAccountDto> getSelectedAccounts() {
        List<UserAccountDto> selected = new ArrayList<UserAccountDto>();

        List<UserAccountDto> all = accountsModel.getObject();
        for (UserAccountDto account : all) {
            if (account.isLoadedOK() && account.getObject().isSelected()) {
                selected.add(account);
            }
        }

        return selected;
    }

    private List<AssignmentEditorDto> getSelectedAssignments() {
        List<AssignmentEditorDto> selected = new ArrayList<AssignmentEditorDto>();

        List<AssignmentEditorDto> all = assignmentsModel.getObject();
        for (AssignmentEditorDto wrapper : all) {
            if (wrapper.isSelected()) {
                selected.add(wrapper);
            }
        }

        return selected;
    }

    private void addSelectedAccountPerformed(AjaxRequestTarget target, List<ResourceType> newResources) {
        ModalWindow window = (ModalWindow) get(MODAL_ID_RESOURCE);
        window.close(target);

        if (newResources.isEmpty()) {
            warn(getString("pageUser.message.noResourceSelected"));
            target.add(getFeedbackPanel());
            return;
        }

        for (ResourceType resource : newResources) {
            try {
                ShadowType shadow = new ShadowType();
                shadow.setResource(resource);

                RefinedResourceSchema refinedSchema = RefinedResourceSchema
                        .getRefinedSchema(resource.asPrismObject(), LayerType.PRESENTATION, getPrismContext());
                if (refinedSchema == null) {
                    error(getString("pageUser.message.couldntCreateAccountNoSchema", resource.getName()));
                    continue;
                }
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace("Refined schema for {}\n{}", resource, refinedSchema.debugDump());
                }

                QName objectClass = refinedSchema.getDefaultRefinedDefinition(ShadowKindType.ACCOUNT)
                        .getObjectClassDefinition().getTypeName();
                shadow.setObjectClass(objectClass);

                getPrismContext().adopt(shadow);

                ObjectWrapper wrapper = ObjectWrapperUtil.createObjectWrapper(
                        WebMiscUtil.getOrigStringFromPoly(resource.getName()), null, shadow.asPrismObject(),
                        ContainerStatus.ADDING, this);
                //                ObjectWrapper wrapper = new ObjectWrapper(WebMiscUtil.getOrigStringFromPoly(resource.getName()), null,
                //                        shadow.asPrismObject(), ContainerStatus.ADDING);
                if (wrapper.getResult() != null && !WebMiscUtil.isSuccessOrHandledError(wrapper.getResult())) {
                    showResultInSession(wrapper.getResult());
                }

                wrapper.setShowEmpty(true);
                wrapper.setMinimalized(false);
                accountsModel.getObject().add(new UserAccountDto(wrapper, UserDtoStatus.ADD));
                setResponsePage(getPage());
            } catch (Exception ex) {
                error(getString("pageUser.message.couldntCreateAccount", resource.getName(), ex.getMessage()));
                LoggingUtils.logException(LOGGER, "Couldn't create account", ex);
            }
        }

        target.add(getFeedbackPanel(), get(createComponentPath(ID_MAIN_FORM, ID_ACCOUNTS)));
    }

    private void addSelectedResourceAssignPerformed(ResourceType resource) {
        AssignmentType assignment = new AssignmentType();
        ConstructionType construction = new ConstructionType();
        assignment.setConstruction(construction);

        try {
            getPrismContext().adopt(assignment, UserType.class, new ItemPath(UserType.F_ASSIGNMENT));
        } catch (SchemaException e) {
            error(getString("Could not create assignment", resource.getName(), e.getMessage()));
            LoggingUtils.logException(LOGGER, "Couldn't create assignment", e);
            return;
        }

        construction.setResource(resource);

        List<AssignmentEditorDto> assignments = assignmentsModel.getObject();
        AssignmentEditorDto dto = new AssignmentEditorDto(resource, AssignmentEditorDtoType.ACCOUNT_CONSTRUCTION,
                UserDtoStatus.ADD, assignment);
        assignments.add(dto);

        dto.setMinimized(false);
        dto.setShowEmpty(true);
    }

    private void addSelectedAssignablePerformed(AjaxRequestTarget target, List<ObjectType> newAssignables) {
        ModalWindow window = (ModalWindow) get(MODAL_ID_ASSIGNABLE);
        window.close(target);

        if (newAssignables.isEmpty()) {
            warn(getString("pageUser.message.noAssignableSelected"));
            target.add(getFeedbackPanel());
            return;
        }

        List<AssignmentEditorDto> assignments = assignmentsModel.getObject();
        for (ObjectType object : newAssignables) {
            try {
                if (object instanceof ResourceType) {
                    addSelectedResourceAssignPerformed((ResourceType) object);
                    continue;
                }

                AssignmentEditorDtoType aType = AssignmentEditorDtoType.getType(object.getClass());

                ObjectReferenceType targetRef = new ObjectReferenceType();
                targetRef.setOid(object.getOid());
                targetRef.setType(aType.getQname());

                AssignmentType assignment = new AssignmentType();
                assignment.setTargetRef(targetRef);

                AssignmentEditorDto dto = new AssignmentEditorDto(object, aType, UserDtoStatus.ADD, assignment);
                dto.setMinimized(false);
                dto.setShowEmpty(true);

                assignments.add(dto);
            } catch (Exception ex) {
                error(getString("pageUser.message.couldntAssignObject", object.getName(), ex.getMessage()));
                LoggingUtils.logException(LOGGER, "Couldn't assign object", ex);
            }
        }

        target.add(getFeedbackPanel(), get(createComponentPath(ID_MAIN_FORM, ID_ASSIGNMENTS)));
    }

    private void updateAccountActivation(AjaxRequestTarget target, List<UserAccountDto> accounts, boolean enabled) {
        if (!isAnyAccountSelected(target)) {
            return;
        }

        for (UserAccountDto account : accounts) {
            if (!account.isLoadedOK()) {
                continue;
            }

            ObjectWrapper wrapper = account.getObject();
            ContainerWrapper activation = wrapper.findContainerWrapper(new ItemPath(ShadowType.F_ACTIVATION));
            if (activation == null) {
                warn(getString("pageUser.message.noActivationFound", wrapper.getDisplayName()));
                continue;
            }

            PropertyWrapper enabledProperty = activation
                    .findPropertyWrapper(ActivationType.F_ADMINISTRATIVE_STATUS);
            if (enabledProperty == null || enabledProperty.getValues().size() != 1) {
                warn(getString("pageUser.message.noEnabledPropertyFound", wrapper.getDisplayName()));
                continue;
            }
            ValueWrapper value = enabledProperty.getValues().get(0);
            ActivationStatusType status = enabled ? ActivationStatusType.ENABLED : ActivationStatusType.DISABLED;
            value.getValue().setValue(status);

            wrapper.setSelected(false);
        }

        target.add(getFeedbackPanel(), get(createComponentPath(ID_MAIN_FORM, ID_ACCOUNTS)));
    }

    private boolean isAnyAccountSelected(AjaxRequestTarget target) {
        List<UserAccountDto> selected = getSelectedAccounts();
        if (selected.isEmpty()) {
            warn(getString("pageUser.message.noAccountSelected"));
            target.add(getFeedbackPanel());
            return false;
        }

        return true;
    }

    private void deleteAccountPerformed(AjaxRequestTarget target) {
        if (!isAnyAccountSelected(target)) {
            return;
        }

        showModalWindow(MODAL_ID_CONFIRM_DELETE_ACCOUNT, target);
    }

    private void showModalWindow(String id, AjaxRequestTarget target) {
        ModalWindow window = (ModalWindow) get(id);
        window.show(target);
    }

    private void deleteAccountConfirmedPerformed(AjaxRequestTarget target, List<UserAccountDto> selected) {
        List<UserAccountDto> accounts = accountsModel.getObject();
        for (UserAccountDto account : selected) {
            if (UserDtoStatus.ADD.equals(account.getStatus())) {
                accounts.remove(account);
            } else {
                account.setStatus(UserDtoStatus.DELETE);
            }
        }
        target.add(get(createComponentPath(ID_MAIN_FORM, ID_ACCOUNTS)));
    }

    private void deleteAssignmentConfirmedPerformed(AjaxRequestTarget target, List<AssignmentEditorDto> selected) {
        List<AssignmentEditorDto> assignments = assignmentsModel.getObject();
        for (AssignmentEditorDto assignment : selected) {
            if (UserDtoStatus.ADD.equals(assignment.getStatus())) {
                assignments.remove(assignment);
            } else {
                assignment.setStatus(UserDtoStatus.DELETE);
                assignment.setSelected(false);
            }
        }

        target.add(getFeedbackPanel(), get(createComponentPath(ID_MAIN_FORM, ID_ASSIGNMENTS)));
    }

    private void unlinkAccountPerformed(AjaxRequestTarget target, List<UserAccountDto> selected) {
        if (!isAnyAccountSelected(target)) {
            return;
        }

        for (UserAccountDto account : selected) {
            if (UserDtoStatus.ADD.equals(account.getStatus())) {
                continue;
            }
            account.setStatus(UserDtoStatus.UNLINK);
        }
        target.add(get(createComponentPath(ID_MAIN_FORM, ID_ACCOUNTS)));
    }

    private void unlockAccountPerformed(AjaxRequestTarget target, List<UserAccountDto> selected) {
        if (!isAnyAccountSelected(target)) {
            return;
        }

        for (UserAccountDto account : selected) {
            // TODO: implement unlock
        }
    }

    private void deleteAssignmentPerformed(AjaxRequestTarget target) {
        List<AssignmentEditorDto> selected = getSelectedAssignments();
        if (selected.isEmpty()) {
            warn(getString("pageUser.message.noAssignmentSelected"));
            target.add(getFeedbackPanel());
            return;
        }

        showModalWindow(MODAL_ID_CONFIRM_DELETE_ASSIGNMENT, target);
    }

    // many things could change (e.g. assignments, tasks) - here we deal only with tasks
    @Override
    public PageBase reinitialize() {
        TablePanel taskTable = (TablePanel) get(createComponentPath(ID_MAIN_FORM, ID_TASKS, ID_TASK_TABLE));
        TaskDtoProvider provider = (TaskDtoProvider) taskTable.getDataTable().getDataProvider();

        provider.clearCache();
        taskTable.modelChanged();
        return this;
    }
}