com.evolveum.midpoint.web.page.admin.users.component.TreeTablePanel.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.web.page.admin.users.component.TreeTablePanel.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.component;

import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.model.api.ModelService;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.delta.ChangeType;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.ReferenceDelta;
import com.evolveum.midpoint.prism.match.PolyStringNormMatchingRule;
import com.evolveum.midpoint.prism.parser.QueryConvertor;
import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.BasicSearchPanel;
import com.evolveum.midpoint.web.component.TabbedPanel;
import com.evolveum.midpoint.web.component.data.ObjectDataProvider;
import com.evolveum.midpoint.web.component.data.TablePanel;
import com.evolveum.midpoint.web.component.data.column.CheckBoxHeaderColumn;
import com.evolveum.midpoint.web.component.data.column.IconColumn;
import com.evolveum.midpoint.web.component.data.column.InlineMenuHeaderColumn;
import com.evolveum.midpoint.web.component.data.column.LinkColumn;
import com.evolveum.midpoint.web.component.dialog.ConfirmationDialog;
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.LoadableModel;
import com.evolveum.midpoint.web.component.util.SimplePanel;
import com.evolveum.midpoint.web.page.PageBase;
import com.evolveum.midpoint.web.page.admin.configuration.component.HeaderMenuAction;
import com.evolveum.midpoint.web.page.admin.users.PageOrgUnit;
import com.evolveum.midpoint.web.page.admin.users.PageUser;
import com.evolveum.midpoint.web.page.admin.users.dto.*;
import com.evolveum.midpoint.web.security.SecurityUtils;
import com.evolveum.midpoint.web.util.ObjectTypeGuiDescriptor;
import com.evolveum.midpoint.web.util.OnePageParameterEncoder;
import com.evolveum.midpoint.web.util.WebMiscUtil;
import com.evolveum.midpoint.web.util.WebModelUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.query_3.QueryType;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
import org.apache.wicket.extensions.markup.html.repeater.tree.ISortableTreeProvider;
import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
import org.apache.wicket.extensions.markup.html.repeater.tree.TableTree;
import org.apache.wicket.extensions.markup.html.repeater.tree.table.TreeColumn;
import org.apache.wicket.extensions.markup.html.repeater.tree.theme.WindowsTheme;
import org.apache.wicket.extensions.markup.html.tabs.ITab;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * todo create function computeHeight() in midpoint.js, update height properly when in "mobile" mode... [lazyman]
 * todo implement midpoint theme for tree [lazyman]
 *
 * @author lazyman
 */
public class TreeTablePanel extends SimplePanel<String> {

    private static final Trace LOGGER = TraceManager.getTrace(TreeTablePanel.class);

    private static final int CONFIRM_DELETE = 0;
    private static final int CONFIRM_DELETE_ROOT = 1;

    private static final String DOT_CLASS = TreeTablePanel.class.getName() + ".";
    private static final String OPERATION_DELETE_OBJECTS = DOT_CLASS + "deleteObjects";
    private static final String OPERATION_DELETE_OBJECT = DOT_CLASS + "deleteObject";
    private static final String OPERATION_MOVE_OBJECTS = DOT_CLASS + "moveObjects";
    private static final String OPERATION_MOVE_OBJECT = DOT_CLASS + "moveObject";
    private static final String OPERATION_UPDATE_OBJECTS = DOT_CLASS + "updateObjects";
    private static final String OPERATION_UPDATE_OBJECT = DOT_CLASS + "updateObject";
    private static final String OPERATION_RECOMPUTE = DOT_CLASS + "recompute";
    private static final String OPERATION_LOAD_PARENTS = DOT_CLASS + "loadParents";

    private static final String ID_TREE = "tree";
    private static final String ID_TREE_CONTAINER = "treeContainer";
    private static final String ID_TABLE = "table";
    private static final String ID_FORM = "form";
    private static final String ID_CONFIRM_DELETE_POPUP = "confirmDeletePopup";
    private static final String ID_MOVE_POPUP = "movePopup";
    private static final String ID_ADD_DELETE_POPUP = "addDeletePopup";
    private static final String ID_TREE_MENU = "treeMenu";
    private static final String ID_TREE_HEADER = "treeHeader";
    private static final String ID_SEARCH_FORM = "searchForm";
    private static final String ID_BASIC_SEARCH = "basicSearch";

    private IModel<OrgTreeDto> selected = new LoadableModel<OrgTreeDto>() {

        @Override
        protected OrgTreeDto load() {
            return getRootFromProvider();
        }
    };

    public TreeTablePanel(String id, IModel<String> rootOid) {
        super(id, rootOid);
    }

    @Override
    protected void initLayout() {
        add(new ConfirmationDialog(ID_CONFIRM_DELETE_POPUP,
                createStringResource("TreeTablePanel.dialog.title.confirmDelete"), createDeleteConfirmString()) {

            @Override
            public void yesPerformed(AjaxRequestTarget target) {
                close(target);

                switch (getConfirmType()) {
                case CONFIRM_DELETE:
                    deleteConfirmedPerformed(target);
                    break;
                case CONFIRM_DELETE_ROOT:
                    deleteRootConfirmedPerformed(target);
                    break;
                }

            }
        });

        add(new OrgUnitBrowser(ID_MOVE_POPUP) {

            @Override
            protected void createRootPerformed(AjaxRequestTarget target) {
                moveConfirmedPerformed(target, null, null, Operation.MOVE);
            }

            @Override
            protected void rowSelected(AjaxRequestTarget target, IModel<OrgTableDto> row, Operation operation) {
                moveConfirmedPerformed(target, selected.getObject(), row.getObject(), operation);
            }

            @Override
            public ObjectQuery createRootQuery() {
                ArrayList<String> oids = new ArrayList<String>();
                ObjectQuery query = new ObjectQuery();

                if (isMovingRoot() && getRootFromProvider() != null) {
                    oids.add(getRootFromProvider().getOid());
                }

                //Selection from table, but only if we are not moving root
                if (!isMovingRoot()) {
                    oids.remove(getRootFromProvider().getOid());

                    List<OrgTableDto> objects = WebMiscUtil.getSelectedData(getTable());
                    if (!objects.isEmpty()) {
                        for (OrgTableDto dto : objects) {
                            oids.add(dto.getOid());

                            //TODO - uncomment this, if parentOrgOid filtering is also required (see issue MID-1780)
                            //oids.addAll(getOrgParentOids(dto.getOid()));
                        }
                    }
                }

                if (oids.isEmpty()) {
                    return null;
                }

                ObjectFilter oidFilter = InOidFilter.createInOid(oids);
                query.setFilter(NotFilter.createNot(oidFilter));

                return query;
            }
        });

        add(new OrgUnitAddDeletePopup(ID_ADD_DELETE_POPUP) {

            @Override
            public void addPerformed(AjaxRequestTarget target, OrgType selected) {
                addOrgUnitToUserPerformed(target, selected);
            }

            @Override
            public void removePerformed(AjaxRequestTarget target, OrgType selected) {
                removeOrgUnitToUserPerformed(target, selected);
            }

            @Override
            public ObjectQuery getAddProviderQuery() {
                return null;
            }

            @Override
            public ObjectQuery getRemoveProviderQuery() {
                return null;
            }
        });

        WebMarkupContainer treeHeader = new WebMarkupContainer(ID_TREE_HEADER);
        treeHeader.setOutputMarkupId(true);
        add(treeHeader);

        InlineMenu treeMenu = new InlineMenu(ID_TREE_MENU, new Model((Serializable) createTreeMenu()));
        treeHeader.add(treeMenu);

        ISortableTreeProvider provider = new OrgTreeProvider(this, getModel());
        List<IColumn<OrgTreeDto, String>> columns = new ArrayList<IColumn<OrgTreeDto, String>>();
        columns.add(new TreeColumn<OrgTreeDto, String>(createStringResource("TreeTablePanel.hierarchy")));

        WebMarkupContainer treeContainer = new WebMarkupContainer(ID_TREE_CONTAINER) {

            @Override
            public void renderHead(IHeaderResponse response) {
                super.renderHead(response);

                //method computes height based on document.innerHeight() - screen height;
                response.render(OnDomReadyHeaderItem.forScript(
                        "updateHeight('" + getMarkupId() + "', ['#" + TreeTablePanel.this.get(ID_FORM).getMarkupId()
                                + "'], ['#" + TreeTablePanel.this.get(ID_TREE_HEADER).getMarkupId() + "'])"));
            }
        };
        add(treeContainer);

        TableTree<OrgTreeDto, String> tree = new TableTree<OrgTreeDto, String>(ID_TREE, columns, provider,
                Integer.MAX_VALUE, new TreeStateModel(provider)) {

            @Override
            protected Component newContentComponent(String id, IModel<OrgTreeDto> model) {
                return new SelectableFolderContent(id, this, model, selected) {

                    @Override
                    protected void onClick(AjaxRequestTarget target) {
                        super.onClick(target);

                        selectTreeItemPerformed(target);
                    }
                };
            }

            @Override
            protected Item<OrgTreeDto> newRowItem(String id, int index, final IModel<OrgTreeDto> model) {
                Item item = super.newRowItem(id, index, model);
                item.add(AttributeModifier.append("class", new AbstractReadOnlyModel<String>() {

                    @Override
                    public String getObject() {
                        OrgTreeDto itemObject = model.getObject();
                        if (itemObject != null && itemObject.equals(selected.getObject())) {
                            return "success";
                        }

                        return null;
                    }
                }));
                return item;
            }
        };
        tree.getTable().add(AttributeModifier.replace("class", "table table-striped table-condensed"));
        tree.add(new WindowsTheme());
        //        tree.add(AttributeModifier.replace("class", "tree-midpoint"));
        treeContainer.add(tree);

        initTable();
        initSearch();
    }

    private List<String> getOrgParentOids(String orgOid) {
        List<String> parentOids = new ArrayList<String>();
        OperationResult result = new OperationResult(OPERATION_LOAD_PARENTS);
        PrismObject<OrgType> org = WebModelUtils.loadObject(OrgType.class, orgOid, result, getPageBase());
        OrgType orgObject = org.asObjectable();

        if (orgObject.getParentOrg() != null && !orgObject.getParentOrg().isEmpty()) {
            for (OrgType parentOrg : orgObject.getParentOrg()) {
                parentOids.add(parentOrg.getOid());
            }
        }

        return parentOids;
    }

    private void initTable() {
        Form form = new Form(ID_FORM);
        form.setOutputMarkupId(true);
        add(form);
        ObjectDataProvider tableProvider = new ObjectDataProvider<OrgTableDto, ObjectType>(this, ObjectType.class) {

            @Override
            public OrgTableDto createDataObjectWrapper(PrismObject<ObjectType> obj) {
                return OrgTableDto.createDto(obj);
            }

            @Override
            public ObjectQuery getQuery() {
                return createTableQuery();
            }
        };
        tableProvider.setOptions(WebModelUtils.createMinimalOptions());
        List<IColumn<OrgTableDto, String>> tableColumns = createTableColumns();
        TablePanel table = new TablePanel(ID_TABLE, tableProvider, tableColumns, 10);
        table.setOutputMarkupId(true);
        form.add(table);
    }

    private void initSearch() {
        Form form = new Form(ID_SEARCH_FORM);
        form.setOutputMarkupId(true);
        add(form);

        BasicSearchPanel basicSearch = new BasicSearchPanel(ID_BASIC_SEARCH, new Model()) {

            @Override
            protected void clearSearchPerformed(AjaxRequestTarget target) {
                clearTableSearchPerformed(target);
            }

            @Override
            protected void searchPerformed(AjaxRequestTarget target) {
                tableSearchPerformed(target);
            }
        };
        form.add(basicSearch);
    }

    private List<InlineMenuItem> createTreeMenu() {
        List<InlineMenuItem> items = new ArrayList<InlineMenuItem>();

        InlineMenuItem item = new InlineMenuItem(createStringResource("TreeTablePanel.collapseAll"),
                new InlineMenuItemAction() {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        collapseAllPerformed(target);
                    }
                });
        items.add(item);
        item = new InlineMenuItem(createStringResource("TreeTablePanel.expandAll"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                expandAllPerformed(target);
            }
        });
        items.add(item);
        items.add(new InlineMenuItem());
        item = new InlineMenuItem(createStringResource("TreeTablePanel.moveRoot"), new InlineMenuItemAction() {

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

        item = new InlineMenuItem(createStringResource("TreeTablePanel.deleteRoot"), new InlineMenuItemAction() {

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

        item = new InlineMenuItem(createStringResource("TreeTablePanel.recomputeRoot"), new InlineMenuItemAction() {

            @Override
            public void onClick(AjaxRequestTarget target) {
                recomputeRootPerformed(target, OrgUnitBrowser.Operation.RECOMPUTE);
            }
        });
        items.add(item);

        item = new InlineMenuItem(createStringResource("TreeTablePanel.editRoot"), new InlineMenuItemAction() {

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

        return items;
    }

    private IModel<String> createDeleteConfirmString() {
        return new AbstractReadOnlyModel<String>() {

            @Override
            public String getObject() {
                ConfirmationDialog dialog = (ConfirmationDialog) TreeTablePanel.this.get(ID_CONFIRM_DELETE_POPUP);
                switch (dialog.getConfirmType()) {
                case CONFIRM_DELETE:
                    return createStringResource("TreeTablePanel.message.deleteObjectConfirm",
                            WebMiscUtil.getSelectedData(getTable()).size()).getString();
                case CONFIRM_DELETE_ROOT:
                    OrgTreeDto dto = getRootFromProvider();

                    return createStringResource("TreeTablePanel.message.deleteRootConfirm", dto.getName(),
                            dto.getDisplayName()).getString();
                }
                return null;
            }
        };
    }

    private OrgTreeDto getRootFromProvider() {
        TableTree<OrgTreeDto, String> tree = getTree();
        ITreeProvider<OrgTreeDto> provider = tree.getProvider();
        Iterator<? extends OrgTreeDto> iterator = provider.getRoots();

        return iterator.hasNext() ? iterator.next() : null;
    }

    private List<IColumn<OrgTableDto, String>> createTableColumns() {
        List<IColumn<OrgTableDto, String>> columns = new ArrayList<IColumn<OrgTableDto, String>>();

        columns.add(new CheckBoxHeaderColumn<OrgTableDto>());
        columns.add(new IconColumn<OrgTableDto>(createStringResource("")) {

            @Override
            protected IModel<String> createIconModel(IModel<OrgTableDto> rowModel) {
                OrgTableDto dto = rowModel.getObject();

                ObjectTypeGuiDescriptor descr = ObjectTypeGuiDescriptor.getDescriptor(dto.getType());
                String icon = descr != null ? descr.getIcon() : ObjectTypeGuiDescriptor.ERROR_ICON;

                return new Model(icon);
            }
        });

        columns.add(
                new LinkColumn<OrgTableDto>(createStringResource("ObjectType.name"), OrgTableDto.F_NAME, "name") {

                    @Override
                    public boolean isEnabled(IModel<OrgTableDto> rowModel) {
                        OrgTableDto dto = rowModel.getObject();
                        return UserType.class.equals(dto.getType()) || OrgType.class.equals(dto.getType());
                    }

                    @Override
                    public void onClick(AjaxRequestTarget target, IModel<OrgTableDto> rowModel) {
                        OrgTableDto dto = rowModel.getObject();
                        if (UserType.class.equals(dto.getType())) {
                            PageParameters parameters = new PageParameters();
                            parameters.add(OnePageParameterEncoder.PARAMETER, dto.getOid());
                            setResponsePage(PageUser.class, parameters);
                        } else if (OrgType.class.equals(dto.getType())) {
                            PageParameters parameters = new PageParameters();
                            parameters.add(OnePageParameterEncoder.PARAMETER, dto.getOid());
                            setResponsePage(PageOrgUnit.class, parameters);
                        }
                    }
                });
        columns.add(new PropertyColumn<OrgTableDto, String>(createStringResource("OrgType.displayName"),
                OrgTableDto.F_DISPLAY_NAME));
        //todo add relation
        columns.add(new PropertyColumn<OrgTableDto, String>(createStringResource("OrgType.identifier"),
                OrgTableDto.F_IDENTIFIER));
        columns.add(new InlineMenuHeaderColumn(initInlineMenu()));

        return columns;
    }

    private List<InlineMenuItem> initInlineMenu() {
        List<InlineMenuItem> headerMenuItems = new ArrayList<InlineMenuItem>();
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.addOrgUnit"), false,
                new HeaderMenuAction(this) {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        addOrgUnitPerformed(target);
                    }
                }));
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.addUser"), false,
                new HeaderMenuAction(this) {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        addUserPerformed(target);
                    }
                }));
        headerMenuItems.add(new InlineMenuItem());

        //TODO - disabled until issue MID-1809 is resolved. Uncomment when finished
        //        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.addToHierarchy"), true,
        //                new HeaderMenuAction(this) {
        //
        //                    @Override
        //                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
        //                        addToHierarchyPerformed(target);
        //                    }
        //                }));
        //        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.removeFromHierarchy"), true,
        //                new HeaderMenuAction(this) {
        //
        //                    @Override
        //                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
        //                        removeFromHierarchyPerformed(target);
        //                    }
        //                }));
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.enable"), true,
                new HeaderMenuAction(this) {

                    @Override
                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                        updateActivationPerformed(target, true);
                    }
                }));
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.disable"), true,
                new HeaderMenuAction(this) {

                    @Override
                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                        updateActivationPerformed(target, false);
                    }
                }));
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.move"), true,
                new HeaderMenuAction(this) {

                    @Override
                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                        movePerformed(target, OrgUnitBrowser.Operation.MOVE);
                    }
                }));
        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.delete"), true,
                new HeaderMenuAction(this) {

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

        headerMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel,menu.recompute"), true,
                new HeaderMenuAction(this) {

                    @Override
                    public void onSubmit(AjaxRequestTarget target, Form<?> form) {
                        recomputePerformed(target, OrgUnitBrowser.Operation.RECOMPUTE);
                    }
                }));

        return headerMenuItems;
    }

    private void addOrgUnitPerformed(AjaxRequestTarget target) {
        PrismObject object = addObjectPerformed(target, new OrgType());
        if (object == null) {
            return;
        }
        PageOrgUnit next = new PageOrgUnit(object);
        setResponsePage(next);
    }

    private void addUserPerformed(AjaxRequestTarget target) {
        PrismObject object = addObjectPerformed(target, new UserType());
        if (object == null) {
            return;
        }
        PageUser next = new PageUser(object);
        setResponsePage(next);
    }

    private PrismObject addObjectPerformed(AjaxRequestTarget target, ObjectType object) {
        PageBase page = getPageBase();
        try {
            ObjectReferenceType ref = new ObjectReferenceType();
            ref.setOid(selected.getObject().getOid());
            ref.setType(OrgType.COMPLEX_TYPE);
            object.getParentOrgRef().add(ref);

            PrismContext context = page.getPrismContext();
            context.adopt(object);

            return object.asPrismContainer();
        } catch (Exception ex) {
            LoggingUtils.logException(LOGGER, "Couldn't create object with parent org. reference", ex);
            page.error("Couldn't create object with parent org. reference, reason: " + ex.getMessage());

            target.add(page.getFeedbackPanel());
        }

        return null;
    }

    /**
     * This method check selection in table.
     */
    private List<OrgTableDto> isAnythingSelected(AjaxRequestTarget target) {
        List<OrgTableDto> objects = WebMiscUtil.getSelectedData(getTable());
        if (objects.isEmpty()) {
            warn(getString("TreeTablePanel.message.nothingSelected"));
            target.add(getPageBase().getFeedbackPanel());
        }

        return objects;
    }

    private void deletePerformed(AjaxRequestTarget target) {
        List<OrgTableDto> objects = isAnythingSelected(target);
        if (objects.isEmpty()) {
            return;
        }

        ConfirmationDialog dialog = (ConfirmationDialog) get(ID_CONFIRM_DELETE_POPUP);
        dialog.setConfirmType(CONFIRM_DELETE);
        dialog.show(target);
    }

    private void deleteConfirmedPerformed(AjaxRequestTarget target) {
        List<OrgTableDto> objects = isAnythingSelected(target);
        if (objects.isEmpty()) {
            return;
        }

        PageBase page = getPageBase();
        OperationResult result = new OperationResult(OPERATION_DELETE_OBJECTS);
        for (OrgTableDto object : objects) {
            OperationResult subResult = result.createSubresult(OPERATION_DELETE_OBJECT);
            WebModelUtils.deleteObject(object.getType(), object.getOid(), subResult, page);
        }
        result.computeStatusComposite();

        page.showResult(result);
        target.add(page.getFeedbackPanel());

        refreshTable(target);
    }

    private void movePerformed(AjaxRequestTarget target, OrgUnitBrowser.Operation operation) {
        movePerformed(target, operation, null, false);
    }

    private void movePerformed(AjaxRequestTarget target, OrgUnitBrowser.Operation operation, OrgTableDto selected,
            boolean movingRoot) {
        List<OrgTableDto> objects;
        if (selected == null) {
            objects = isAnythingSelected(target);
            if (objects.isEmpty()) {
                return;
            }
        } else {
            objects = new ArrayList<>();
            objects.add(selected);
        }

        OrgUnitBrowser dialog = (OrgUnitBrowser) get(ID_MOVE_POPUP);
        dialog.setMovingRoot(movingRoot);
        dialog.setOperation(operation);
        dialog.setSelectedObjects(objects);
        dialog.show(target);
    }

    private PrismReferenceValue createPrismRefValue(OrgDto dto) {
        PrismReferenceValue value = new PrismReferenceValue();
        value.setOid(dto.getOid());
        value.setRelation(dto.getRelation());
        value.setTargetType(ObjectTypes.getObjectType(dto.getType()).getTypeQName());
        return value;
    }

    private ObjectDelta createMoveDelta(PrismObject<OrgType> orgUnit, OrgTreeDto oldParent, OrgTableDto newParent,
            OrgUnitBrowser.Operation operation) {
        ObjectDelta delta = orgUnit.createDelta(ChangeType.MODIFY);
        PrismReferenceDefinition refDef = orgUnit.getDefinition().findReferenceDefinition(OrgType.F_PARENT_ORG_REF);
        ReferenceDelta refDelta = delta.createReferenceModification(OrgType.F_PARENT_ORG_REF, refDef);
        PrismReferenceValue value;
        switch (operation) {
        case ADD:
            LOGGER.debug("Adding new parent {}", new Object[] { newParent });
            //adding parentRef newParent to orgUnit
            value = createPrismRefValue(newParent);
            refDelta.addValuesToAdd(value);
            break;
        case REMOVE:
            LOGGER.debug("Removing new parent {}", new Object[] { newParent });
            //removing parentRef newParent from orgUnit
            value = createPrismRefValue(newParent);
            refDelta.addValuesToDelete(value);
            break;
        case MOVE:
            if (oldParent == null && newParent == null) {
                LOGGER.debug("Moving to root");
                //moving orgUnit to root, removing all parentRefs
                PrismReference ref = orgUnit.findReference(OrgType.F_PARENT_ORG_REF);
                if (ref != null) {
                    for (PrismReferenceValue val : ref.getValues()) {
                        refDelta.addValuesToDelete(val.clone());
                    }
                }
            } else {
                LOGGER.debug("Adding new parent {}, removing old parent {}", new Object[] { newParent, oldParent });
                //moving from old to new, removing oldParent adding newParent refs
                value = createPrismRefValue(newParent);
                refDelta.addValuesToAdd(value);

                value = createPrismRefValue(oldParent);
                refDelta.addValuesToDelete(value);
            }
            break;
        }

        return delta;
    }

    private void moveConfirmedPerformed(AjaxRequestTarget target, OrgTreeDto oldParent, OrgTableDto newParent,
            OrgUnitBrowser.Operation operation) {
        OrgUnitBrowser dialog = (OrgUnitBrowser) get(ID_MOVE_POPUP);
        List<OrgTableDto> objects = dialog.getSelected();

        PageBase page = getPageBase();
        ModelService model = page.getModelService();
        OperationResult result = new OperationResult(OPERATION_MOVE_OBJECTS);
        for (OrgTableDto object : objects) {
            OperationResult subResult = result.createSubresult(OPERATION_MOVE_OBJECT);

            PrismObject<OrgType> orgUnit = WebModelUtils.loadObject(OrgType.class, object.getOid(),
                    WebModelUtils.createOptionsForParentOrgRefs(), subResult, getPageBase());
            try {
                ObjectDelta delta = createMoveDelta(orgUnit, oldParent, newParent, operation);

                model.executeChanges(WebMiscUtil.createDeltaCollection(delta), null,
                        page.createSimpleTask(OPERATION_MOVE_OBJECT), subResult);
            } catch (Exception ex) {
                subResult.recordFatalError("Couldn't move object " + null + " to " + null + ".", ex);
                LoggingUtils.logException(LOGGER, "Couldn't move object {} to {}", ex, object.getName());
            } finally {
                subResult.computeStatusIfUnknown();
            }
        }
        result.computeStatusComposite();

        ObjectDataProvider provider = (ObjectDataProvider) getTable().getDataTable().getDataProvider();
        provider.clearCache();

        page.showResult(result);
        dialog.close(target);

        refreshTabbedPanel(target);
    }

    private void refreshTabbedPanel(AjaxRequestTarget target) {
        PageBase page = getPageBase();

        TabbedPanel tabbedPanel = findParent(TabbedPanel.class);
        IModel<List<ITab>> tabs = tabbedPanel.getTabs();

        if (tabs instanceof LoadableModel) {
            ((LoadableModel) tabs).reset();
        }

        tabbedPanel.setSelectedTab(0);

        target.add(tabbedPanel);
        target.add(page.getFeedbackPanel());
    }

    private TableTree getTree() {
        return (TableTree) get(createComponentPath(ID_TREE_CONTAINER, ID_TREE));
    }

    private TablePanel getTable() {
        return (TablePanel) get(createComponentPath(ID_FORM, ID_TABLE));
    }

    private void selectTreeItemPerformed(AjaxRequestTarget target) {
        BasicSearchPanel<String> basicSearch = (BasicSearchPanel) get(
                createComponentPath(ID_SEARCH_FORM, ID_BASIC_SEARCH));
        basicSearch.getModel().setObject(null);

        TablePanel table = getTable();
        table.setCurrentPage(null);

        target.add(table);
        target.add(get(ID_SEARCH_FORM));
    }

    private ObjectQuery createTableQuery() {
        OrgTreeDto dto = selected.getObject();
        String oid = dto != null ? dto.getOid() : getModel().getObject();

        OrgFilter org = OrgFilter.createOrg(oid, OrgFilter.Scope.ONE_LEVEL);

        BasicSearchPanel<String> basicSearch = (BasicSearchPanel) get(
                createComponentPath(ID_SEARCH_FORM, ID_BASIC_SEARCH));
        String object = basicSearch.getModelObject();
        if (StringUtils.isEmpty(object)) {
            return ObjectQuery.createObjectQuery(org);
        }

        PageBase page = getPageBase();
        PrismContext context = page.getPrismContext();

        PolyStringNormalizer normalizer = context.getDefaultPolyStringNormalizer();
        String normalizedString = normalizer.normalize(object);
        if (StringUtils.isEmpty(normalizedString)) {
            return ObjectQuery.createObjectQuery(org);
        }

        SubstringFilter substring = SubstringFilter.createSubstring(ObjectType.F_NAME, ObjectType.class, context,
                PolyStringNormMatchingRule.NAME, normalizedString);

        AndFilter and = AndFilter.createAnd(org, substring);

        return ObjectQuery.createObjectQuery(and);
    }

    private void collapseAllPerformed(AjaxRequestTarget target) {
        TableTree tree = getTree();
        TreeStateModel model = (TreeStateModel) tree.getDefaultModel();
        model.collapseAll();

        target.add(tree);
    }

    private void expandAllPerformed(AjaxRequestTarget target) {
        TableTree tree = getTree();
        TreeStateModel model = (TreeStateModel) tree.getDefaultModel();
        model.expandAll();

        target.add(tree);
    }

    private void moveRootPerformed(AjaxRequestTarget target) {
        OrgTreeDto root = getRootFromProvider();
        OrgTableDto dto = new OrgTableDto(root.getOid(), root.getType());
        movePerformed(target, OrgUnitBrowser.Operation.MOVE, dto, true);
    }

    private void updateActivationPerformed(AjaxRequestTarget target, boolean enable) {
        List<OrgTableDto> objects = isAnythingSelected(target);
        if (objects.isEmpty()) {
            return;
        }

        PageBase page = getPageBase();
        OperationResult result = new OperationResult(OPERATION_UPDATE_OBJECTS);
        for (OrgTableDto object : objects) {
            if (!(FocusType.class.isAssignableFrom(object.getType()))) {
                continue;
            }

            OperationResult subResult = result.createSubresult(OPERATION_UPDATE_OBJECT);
            ObjectDelta delta = WebModelUtils.createActivationAdminStatusDelta(object.getType(), object.getOid(),
                    enable, page.getPrismContext());

            WebModelUtils.save(delta, subResult, page);
        }
        result.computeStatusComposite();

        page.showResult(result);
        target.add(page.getFeedbackPanel());

        refreshTable(target);
    }

    private void refreshTable(AjaxRequestTarget target) {
        ObjectDataProvider provider = (ObjectDataProvider) getTable().getDataTable().getDataProvider();
        provider.clearCache();

        target.add(getTable());
    }

    private void recomputeRootPerformed(AjaxRequestTarget target, OrgUnitBrowser.Operation operation) {
        OrgTreeDto root = getRootFromProvider();
        OrgTableDto dto = new OrgTableDto(root.getOid(), root.getType());
        recomputePerformed(target, operation, dto);
    }

    private void recomputePerformed(AjaxRequestTarget target, OrgUnitBrowser.Operation operation) {
        recomputePerformed(target, operation, null);
    }

    private void recomputePerformed(AjaxRequestTarget target, OrgUnitBrowser.Operation operation,
            OrgTableDto orgDto) {
        List<OrgTableDto> objects;
        if (orgDto == null) {
            objects = isAnythingSelected(target);
            if (objects.isEmpty()) {
                return;
            }
        } else {
            objects = new ArrayList<>();
            objects.add(orgDto);
        }

        Task task = getPageBase().createSimpleTask(OPERATION_RECOMPUTE);
        OperationResult result = new OperationResult(OPERATION_RECOMPUTE);

        try {
            for (OrgTableDto org : objects) {

                PrismObject<TaskType> recomputeTask = prepareRecomputeTask(org);

                ObjectDelta taskDelta = ObjectDelta.createAddDelta(recomputeTask);

                if (LOGGER.isTraceEnabled()) {
                    LOGGER.trace(taskDelta.debugDump());
                }

                ObjectDelta emptyDelta = ObjectDelta.createEmptyModifyDelta(OrgType.class, org.getOid(),
                        getPageBase().getPrismContext());
                ModelExecuteOptions options = new ModelExecuteOptions();
                options.setReconcile(true);
                getPageBase().getModelService().executeChanges(
                        WebMiscUtil.createDeltaCollection(emptyDelta, taskDelta), options, task, result);
            }

            result.recordSuccess();
        } catch (Exception e) {
            result.recordFatalError(getString("TreeTablePanel.message.recomputeError"), e);
            LoggingUtils.logException(LOGGER, getString("TreeTablePanel.message.recomputeError"), e);
        }

        getPageBase().showResult(result);
        target.add(getPageBase().getFeedbackPanel());
        refreshTabbedPanel(target);
    }

    private PrismObject<TaskType> prepareRecomputeTask(OrgTableDto org) throws SchemaException {
        PrismPropertyDefinition propertyDef = getPageBase().getPrismContext().getSchemaRegistry()
                .findPropertyDefinitionByElementName(SchemaConstants.MODEL_EXTENSION_OBJECT_QUERY);

        ObjectFilter refFilter = RefFilter.createReferenceEqual(UserType.F_PARENT_ORG_REF, UserType.class,
                getPageBase().getPrismContext(), org.getOid());

        SearchFilterType filterType = QueryConvertor.createSearchFilterType(refFilter,
                getPageBase().getPrismContext());
        QueryType queryType = new QueryType();
        queryType.setFilter(filterType);

        PrismProperty<QueryType> property = propertyDef.instantiate();
        property.setRealValue(queryType);

        TaskType taskType = new TaskType();

        taskType.setName(WebMiscUtil.createPolyFromOrigString(
                createStringResource("TreeTablePanel.recomputeTask", org.getName()).getString()));
        taskType.setBinding(TaskBindingType.LOOSE);
        taskType.setExecutionStatus(TaskExecutionStatusType.RUNNABLE);
        taskType.setRecurrence(TaskRecurrenceType.SINGLE);

        MidPointPrincipal owner = SecurityUtils.getPrincipalUser();

        ObjectReferenceType ownerRef = new ObjectReferenceType();
        ownerRef.setOid(owner.getOid());
        ownerRef.setType(owner.getUser().COMPLEX_TYPE);
        taskType.setOwnerRef(ownerRef);

        ExtensionType extensionType = new ExtensionType();
        taskType.setExtension(extensionType);

        getPageBase().getPrismContext().adopt(taskType);

        extensionType.asPrismContainerValue().add(property);

        taskType.setHandlerUri(
                "http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/recompute/handler-3");

        return taskType.asPrismObject();
    }

    private void deleteRootPerformed(AjaxRequestTarget target) {
        if (selected.getObject() == null) {
            warn(getString("TreeTablePanel.message.nothingSelected"));
            target.add(getPageBase().getFeedbackPanel());
            return;
        }

        ConfirmationDialog dialog = (ConfirmationDialog) get(ID_CONFIRM_DELETE_POPUP);
        dialog.setConfirmType(CONFIRM_DELETE_ROOT);
        dialog.show(target);
    }

    private void deleteRootConfirmedPerformed(AjaxRequestTarget target) {
        OperationResult result = new OperationResult(OPERATION_DELETE_OBJECT);

        PageBase page = getPageBase();

        OrgTreeDto dto = getRootFromProvider();
        WebModelUtils.deleteObject(OrgType.class, dto.getOid(), result, page);

        result.computeStatusIfUnknown();
        page.showResultInSession(result);

        refreshTabbedPanel(target);
    }

    private void clearTableSearchPerformed(AjaxRequestTarget target) {
        BasicSearchPanel basicSearch = (BasicSearchPanel) get(createComponentPath(ID_SEARCH_FORM, ID_BASIC_SEARCH));
        basicSearch.getModel().setObject(null);

        refreshTable(target);
    }

    private void tableSearchPerformed(AjaxRequestTarget target) {
        refreshTable(target);
    }

    private static class TreeStateModel extends AbstractReadOnlyModel<Set<OrgTreeDto>> {

        private TreeStateSet<OrgTreeDto> set = new TreeStateSet<OrgTreeDto>();
        private ISortableTreeProvider provider;

        TreeStateModel(ISortableTreeProvider provider) {
            this.provider = provider;
        }

        @Override
        public Set<OrgTreeDto> getObject() {
            //just to have root expanded at all time
            if (set.isEmpty()) {
                Iterator<OrgTreeDto> iterator = provider.getRoots();
                if (iterator.hasNext()) {
                    set.add(iterator.next());
                }

            }
            return set;
        }

        public void expandAll() {
            set.expandAll();
        }

        public void collapseAll() {
            set.collapseAll();
        }
    }

    private void editRootPerformed(AjaxRequestTarget target) {
        OrgTreeDto root = getRootFromProvider();
        PageParameters parameters = new PageParameters();
        parameters.add(OnePageParameterEncoder.PARAMETER, root.getOid());
        setResponsePage(PageOrgUnit.class, parameters);
    }

    private void addToHierarchyPerformed(AjaxRequestTarget target) {
        showAddDeletePopup(target, OrgUnitAddDeletePopup.ActionState.ADD);
    }

    private void removeFromHierarchyPerformed(AjaxRequestTarget target) {
        showAddDeletePopup(target, OrgUnitAddDeletePopup.ActionState.DELETE);
    }

    private void showAddDeletePopup(AjaxRequestTarget target, OrgUnitAddDeletePopup.ActionState state) {
        OrgUnitAddDeletePopup dialog = (OrgUnitAddDeletePopup) get(ID_ADD_DELETE_POPUP);
        dialog.setState(state, target);

        dialog.show(target);
    }

    private void addOrgUnitToUserPerformed(AjaxRequestTarget target, OrgType org) {
        //TODO
    }

    private void removeOrgUnitToUserPerformed(AjaxRequestTarget target, OrgType org) {
        //TODO
    }
}