com.evolveum.midpoint.web.page.admin.roles.AbstractRoleMemberPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.web.page.admin.roles.AbstractRoleMemberPanel.java

Source

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.query.QueryFactory;
import com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty;
import com.evolveum.midpoint.web.component.AjaxIconButton;
import com.evolveum.midpoint.web.component.menu.cog.ButtonInlineMenuItem;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItemAction;
import com.evolveum.midpoint.web.page.admin.configuration.component.HeaderMenuAction;
import com.evolveum.midpoint.web.session.MemberPanelStorage;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.collections.CollectionUtils;
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.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

import com.evolveum.midpoint.gui.api.GuiStyleConstants;
import com.evolveum.midpoint.gui.api.component.BasePanel;
import com.evolveum.midpoint.gui.api.component.MainObjectListPanel;
import com.evolveum.midpoint.gui.api.page.PageBase;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.dialog.ChooseFocusTypeAndRelationDialogPanel;
import com.evolveum.midpoint.web.component.form.CheckFormGroup;
import com.evolveum.midpoint.web.component.form.DropDownFormGroup;
import com.evolveum.midpoint.web.component.input.QNameObjectTypeChoiceRenderer;
import com.evolveum.midpoint.web.component.input.RelationDropDownChoicePanel;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem;
import com.evolveum.midpoint.web.component.search.Search;
import com.evolveum.midpoint.web.component.search.SearchFactory;
import com.evolveum.midpoint.web.component.util.EnableBehaviour;
import com.evolveum.midpoint.web.component.util.SelectableBean;
import com.evolveum.midpoint.web.component.util.VisibleBehaviour;
import com.evolveum.midpoint.web.page.admin.configuration.component.ChooseTypePanel;
import com.evolveum.midpoint.web.page.admin.dto.ObjectViewDto;
import com.evolveum.midpoint.web.security.GuiAuthorizationConstants;
import com.evolveum.midpoint.web.session.UserProfileStorage.TableId;

import static com.evolveum.midpoint.gui.api.util.WebComponentUtil.isAuthorized;

public abstract class AbstractRoleMemberPanel<R extends AbstractRoleType> extends BasePanel<R> {

    private static final long serialVersionUID = 1L;

    protected enum QueryScope {
        SELECTED, ALL, ALL_DIRECT
    }

    protected enum MemberOperation {
        ADD, REMOVE, RECOMPUTE
    }

    private static final Trace LOGGER = TraceManager.getTrace(AbstractRoleMemberPanel.class);
    private static final String DOT_CLASS = AbstractRoleMemberPanel.class.getName() + ".";
    protected static final String OPERATION_RELATION_DEFINITION_TYPE = DOT_CLASS + "loadRelationDefinitionTypes";

    protected static final String ID_FORM = "form";

    protected static final String ID_CONTAINER_MEMBER = "memberContainer";
    protected static final String ID_CHILD_TABLE = "childUnitTable";
    protected static final String ID_MEMBER_TABLE = "memberTable";

    private static final String ID_OBJECT_TYPE = "type";
    private static final String ID_TENANT = "tenant";
    private static final String ID_PROJECT = "project";
    private static final String ID_INDIRECT_MEMBERS = "indirectMembers";

    protected static final String ID_SEARCH_SCOPE = "searchScope";
    protected SearchBoxScopeType scopeDefaultValue = null;
    protected QName objectTypeDefaultValue = null;

    protected static final String ID_SEARCH_BY_RELATION = "searchByRelation";

    private static Map<QName, Map<String, String>> authorizations = new HashMap<>();
    private static Map<QName, TableId> tablesId = new HashMap<>();

    static {
        tablesId.put(RoleType.COMPLEX_TYPE, TableId.ROLE_MEMEBER_PANEL);
        tablesId.put(ServiceType.COMPLEX_TYPE, TableId.SERVICE_MEMEBER_PANEL);
        tablesId.put(OrgType.COMPLEX_TYPE, TableId.ORG_MEMEBER_PANEL);
    }

    static {
        authorizations.put(RoleType.COMPLEX_TYPE, GuiAuthorizationConstants.ROLE_MEMBERS_AUTHORIZATIONS);
        authorizations.put(ServiceType.COMPLEX_TYPE, GuiAuthorizationConstants.SERVICE_MEMBERS_AUTHORIZATIONS);
        authorizations.put(OrgType.COMPLEX_TYPE, GuiAuthorizationConstants.ORG_MEMBERS_AUTHORIZATIONS);
    }

    public AbstractRoleMemberPanel(String id, IModel<R> model) {
        super(id, model);
    }

    @Override
    protected void onInitialize() {
        super.onInitialize();
        initLayout();
    }

    protected void initLayout() {
        Form<?> form = new com.evolveum.midpoint.web.component.form.Form(ID_FORM);
        form.setOutputMarkupId(true);
        add(form);
        initDefaultSearchParameters();
        initSearch(form);
        initMemberTable(form);
        setOutputMarkupId(true);

    }

    private void initDefaultSearchParameters() {
        GuiObjectListPanelConfigurationType additionalPanel = getAdditionalPanelConfig();
        if (additionalPanel != null && additionalPanel.getSearchBoxConfiguration() != null) {
            scopeDefaultValue = additionalPanel.getSearchBoxConfiguration().getDefaultScope();
            objectTypeDefaultValue = additionalPanel.getSearchBoxConfiguration().getDefaultObjectType();
        }
        if (scopeDefaultValue == null) {
            scopeDefaultValue = SearchBoxScopeType.ONE_LEVEL;
        }
        if (objectTypeDefaultValue == null) {
            objectTypeDefaultValue = WebComponentUtil.classToQName(getPrismContext(), getDefaultObjectType());
        }
        if (getMemberPanelStorage() != null) {
            if (getMemberPanelStorage().getOrgSearchScope() == null) {
                getMemberPanelStorage().setOrgSearchScope(scopeDefaultValue);
            }
            if (getMemberPanelStorage().getType() == null) {
                getMemberPanelStorage().setType(ObjectTypes.getObjectType(objectTypeDefaultValue.getLocalPart()));
            }
        }
    }

    protected Form<?> getForm() {
        return (Form) get(ID_FORM);
    }

    private void initMemberTable(Form<?> form) {
        WebMarkupContainer memberContainer = new WebMarkupContainer(ID_CONTAINER_MEMBER);
        memberContainer.setOutputMarkupId(true);
        memberContainer.setOutputMarkupPlaceholderTag(true);
        form.add(memberContainer);

        PageBase pageBase = getPageBase();
        MainObjectListPanel<ObjectType> childrenListPanel = new MainObjectListPanel<ObjectType>(ID_MEMBER_TABLE,
                ObjectType.class, getTableId(getComplexTypeQName()), getSearchOptions(), pageBase) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void objectDetailsPerformed(AjaxRequestTarget target, ObjectType object) {
                detailsPerformed(target, object);
            }

            @Override
            protected boolean isClickable(IModel<SelectableBean<ObjectType>> rowModel) {
                if (rowModel == null || rowModel.getObject() == null || rowModel.getObject().getValue() == null) {
                    return false;
                }
                Class<?> objectClass = rowModel.getObject().getValue().getClass();
                return WebComponentUtil.hasDetailsPage(objectClass);
            }

            @Override
            protected void newObjectPerformed(AjaxRequestTarget target) {
                AbstractRoleMemberPanel.this.createFocusMemberPerformed(target);
            }

            @Override
            protected List<Component> createToolbarButtonsList(String buttonId) {
                List<Component> buttonsList = super.createToolbarButtonsList(buttonId);
                AjaxIconButton assignButton = new AjaxIconButton(buttonId,
                        new Model<>(GuiStyleConstants.CLASS_ASSIGN), //TODO change icon class
                        createStringResource("TreeTablePanel.menu.addMembers")) {

                    private static final long serialVersionUID = 1L;

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        AbstractRoleMemberPanel.this.assignMembers(target, getSupportedRelations());
                    }
                };
                assignButton.add(AttributeAppender.append("class", "btn btn-default btn-sm"));
                buttonsList.add(1, assignButton);
                return buttonsList;
            }

            @Override
            protected List<IColumn<SelectableBean<ObjectType>, String>> createColumns() {
                return createMembersColumns();
            }

            @Override
            protected List<InlineMenuItem> createInlineMenu() {
                return createRowActions();
            }

            @Override
            protected Search createSearch() {
                return SearchFactory.createSearch(getDefaultObjectType(), pageBase);
            }

            @Override
            protected ObjectQuery createContentQuery() {
                ObjectQuery q = super.createContentQuery();

                ObjectQuery members = AbstractRoleMemberPanel.this.createContentQuery();

                List<ObjectFilter> filters = new ArrayList<>();

                if (q != null && q.getFilter() != null) {
                    filters.add(q.getFilter());
                }

                if (members != null && members.getFilter() != null) {
                    filters.add(members.getFilter());
                }

                QueryFactory queryFactory = pageBase.getPrismContext().queryFactory();
                if (filters.size() == 1) {
                    return queryFactory.createQuery(filters.iterator().next());
                } else {
                    return queryFactory.createQuery(queryFactory.createAnd(filters));
                }
            }

            @Override
            protected GuiObjectListPanelConfigurationType getAdditionalPanelConfig() {
                return AbstractRoleMemberPanel.this.getAdditionalPanelConfig();
            }
        };
        childrenListPanel.setOutputMarkupId(true);
        memberContainer.add(childrenListPanel);
    }

    protected TableId getTableId(QName complextType) {
        return tablesId.get(complextType);
    }

    protected Map<String, String> getAuthorizations(QName complexType) {
        return authorizations.get(complexType);
    }

    protected QName getComplexTypeQName() {
        return getModelObject().asPrismObject().getComplexTypeDefinition().getTypeName();
    }

    private List<InlineMenuItem> createRowActions() {
        List<InlineMenuItem> menu = new ArrayList<>();
        if (isAuthorized(GuiAuthorizationConstants.MEMBER_OPERATION_ASSIGN)) {
            menu.add(new InlineMenuItem(createStringResource("abstractRoleMemberPanel.menu.assign")) {
                private static final long serialVersionUID = 1L;

                @Override
                public InlineMenuItemAction initAction() {
                    return new HeaderMenuAction(AbstractRoleMemberPanel.this) {
                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            assignMembers(target, getSupportedRelations());
                        }
                    };
                }
            });
        }

        if (isAuthorized(GuiAuthorizationConstants.MEMBER_OPERATION_UNASSIGN)) {
            menu.add(new ButtonInlineMenuItem(createStringResource("abstractRoleMemberPanel.menu.unassign")) {
                private static final long serialVersionUID = 1L;

                @Override
                public InlineMenuItemAction initAction() {
                    return new HeaderMenuAction(AbstractRoleMemberPanel.this) {
                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            unassignMembersPerformed(target);
                        }
                    };

                }

                @Override
                public String getButtonIconCssClass() {
                    return GuiStyleConstants.CLASS_UNASSIGN;
                }
            });
        }

        if (isAuthorized(GuiAuthorizationConstants.MEMBER_OPERATION_RECOMPUTE)) {
            menu.add(new ButtonInlineMenuItem(createStringResource("abstractRoleMemberPanel.menu.recompute")) {
                private static final long serialVersionUID = 1L;

                @Override
                public InlineMenuItemAction initAction() {
                    return new HeaderMenuAction(AbstractRoleMemberPanel.this) {
                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            recomputeMembersPerformed(target);
                        }
                    };
                }

                @Override
                public IModel<String> getConfirmationMessageModel() {
                    return getMemberTable().getSelectedObjectsCount() > 0
                            ? createStringResource(
                                    "abstractRoleMemberPanel.recomputeSelectedMembersConfirmationLabel")
                            : createStringResource("abstractRoleMemberPanel.recomputeAllMembersConfirmationLabel");
                }

                @Override
                public String getButtonIconCssClass() {
                    return GuiStyleConstants.CLASS_RECONCILE_MENU_ITEM;
                }

            });
        }
        if (isAuthorized(GuiAuthorizationConstants.MEMBER_OPERATION_CREATE)) {
            menu.add(new InlineMenuItem(createStringResource("abstractRoleMemberPanel.menu.create")) {
                private static final long serialVersionUID = 1L;

                @Override
                public InlineMenuItemAction initAction() {
                    return new HeaderMenuAction(AbstractRoleMemberPanel.this) {
                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            createFocusMemberPerformed(target);
                        }
                    };
                }
            });
        }
        if (isAuthorized(GuiAuthorizationConstants.MEMBER_OPERATION_DELETE)) {
            menu.add(new InlineMenuItem(createStringResource("abstractRoleMemberPanel.menu.delete")) {
                private static final long serialVersionUID = 1L;

                @Override
                public InlineMenuItemAction initAction() {
                    return new HeaderMenuAction(AbstractRoleMemberPanel.this) {
                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            deleteMembersPerformed(target);
                        }
                    };
                }

            });
        }
        return menu;
    }

    protected abstract List<QName> getSupportedRelations();

    protected GuiObjectListPanelConfigurationType getAdditionalPanelConfig() {
        return null;
    }

    private boolean isAuthorized(String action) {
        Map<String, String> memeberAuthz = getAuthorizations(getComplexTypeQName());
        return WebComponentUtil.isAuthorized(memeberAuthz.get(action));
    }

    protected <O extends ObjectType> void assignMembers(AjaxRequestTarget target,
            List<QName> availableRelationList) {
        MemberOperationsHelper.assignMembers(getPageBase(), getModelObject(), target, availableRelationList);
    }

    private void unassignMembersPerformed(AjaxRequestTarget target) {
        QueryScope scope = getQueryScope(false);

        ChooseFocusTypeAndRelationDialogPanel chooseTypePopupContent = new ChooseFocusTypeAndRelationDialogPanel(
                getPageBase().getMainPopupBodyId(),
                createStringResource("abstractRoleMemberPanel.unassignAllMembersConfirmationLabel")) {
            private static final long serialVersionUID = 1L;

            @Override
            protected List<QName> getSupportedObjectTypes() {
                return AbstractRoleMemberPanel.this.getSupportedObjectTypes(true);
            }

            @Override
            protected List<QName> getSupportedRelations() {
                return AbstractRoleMemberPanel.this.getSupportedRelations();
            }

            @Override
            protected boolean isFocusTypeSelectorVisible() {
                return !QueryScope.SELECTED.equals(scope);
            }

            protected void okPerformed(QName type, Collection<QName> relations, AjaxRequestTarget target) {
                unassignMembersPerformed(type, scope, relations, target);

            }
        };

        getPageBase().showMainPopup(chooseTypePopupContent, target);
    }

    private void deleteMembersPerformed(AjaxRequestTarget target) {
        QueryScope scope = getQueryScope(false);
        ChooseFocusTypeAndRelationDialogPanel chooseTypePopupContent = new ChooseFocusTypeAndRelationDialogPanel(
                getPageBase().getMainPopupBodyId(),
                createStringResource("abstractRoleMemberPanel.deleteAllMembersConfirmationLabel")) {
            private static final long serialVersionUID = 1L;

            @Override
            protected List<QName> getSupportedObjectTypes() {
                return AbstractRoleMemberPanel.this.getSupportedObjectTypes(true);
            }

            @Override
            protected List<QName> getSupportedRelations() {
                return AbstractRoleMemberPanel.this.getSupportedRelations();
            }

            protected void okPerformed(QName type, Collection<QName> relations, AjaxRequestTarget target) {
                deleteMembersPerformed(type, scope, relations, target);

            }

            @Override
            protected boolean isFocusTypeSelectorVisible() {
                return !QueryScope.SELECTED.equals(scope);
            }

        };

        getPageBase().showMainPopup(chooseTypePopupContent, target);

    }

    protected void createFocusMemberPerformed(AjaxRequestTarget target) {

        ChooseFocusTypeAndRelationDialogPanel chooseTypePopupContent = new ChooseFocusTypeAndRelationDialogPanel(
                getPageBase().getMainPopupBodyId()) {
            private static final long serialVersionUID = 1L;

            @Override
            protected List<QName> getSupportedObjectTypes() {
                return AbstractRoleMemberPanel.this.getSupportedObjectTypes(false);
            }

            @Override
            protected List<QName> getSupportedRelations() {
                return AbstractRoleMemberPanel.this.getSupportedRelations();
            }

            protected void okPerformed(QName type, Collection<QName> relations, AjaxRequestTarget target) {
                if (relations == null || relations.isEmpty()) {
                    getSession().warn("No relation was selected. Cannot create member");
                    target.add(this);
                    target.add(getPageBase().getFeedbackPanel());
                    return;
                }
                try {
                    MemberOperationsHelper.initObjectForAdd(AbstractRoleMemberPanel.this.getPageBase(),
                            AbstractRoleMemberPanel.this.getModelObject(), type, relations, target);
                } catch (SchemaException e) {
                    throw new SystemException(e.getMessage(), e);
                }

            };
        };

        getPageBase().showMainPopup(chooseTypePopupContent, target);

    }

    protected void deleteMembersPerformed(QName type, QueryScope scope, Collection<QName> relations,
            AjaxRequestTarget target) {
        if (relations == null || relations.isEmpty()) {
            getSession().warn("No relation was selected. Cannot perform delete members");
            target.add(this);
            target.add(getPageBase().getFeedbackPanel());
            return;
        }
        MemberOperationsHelper.deleteMembersPerformed(getPageBase(), scope, getActionQuery(scope, relations), type,
                target);
    }

    protected void unassignMembersPerformed(QName type, QueryScope scope, Collection<QName> relations,
            AjaxRequestTarget target) {
        if (relations == null || relations.isEmpty()) {
            getSession().warn("No relation was selected. Cannot perform unassign members");
            target.add(this);
            target.add(getPageBase().getFeedbackPanel());
            return;
        }
        MemberOperationsHelper.unassignMembersPerformed(getPageBase(), getModelObject(), scope,
                getActionQuery(scope, relations), relations, type, target);
    }

    private ObjectViewDto<OrgType> getParameter(String panelId) {
        ChooseTypePanel<OrgType> tenantChoice = (ChooseTypePanel) get(createComponentPath(ID_FORM, panelId));
        return tenantChoice.getModelObject();
    }

    protected ObjectQuery getActionQuery(QueryScope scope, Collection<QName> relations) {
        switch (scope) {
        case ALL:
            return createAllMemberQuery(relations);
        case ALL_DIRECT:
            return MemberOperationsHelper.createDirectMemberQuery(getModelObject(), getSearchType().getTypeQName(),
                    relations, getParameter(ID_TENANT), getParameter(ID_PROJECT), getPrismContext());
        case SELECTED:
            return MemberOperationsHelper.createSelectedObjectsQuery(getMemberTable().getSelectedObjects(),
                    getPrismContext());
        }

        return null;
    }

    protected void initSearch(Form<?> form) {

        List<SearchBoxScopeType> scopeValues = Arrays.asList(SearchBoxScopeType.values());
        DropDownFormGroup<SearchBoxScopeType> searchScrope = createDropDown(ID_SEARCH_SCOPE,
                Model.of(getMemberPanelStorage() != null ? getMemberPanelStorage().getOrgSearchScope()
                        : scopeDefaultValue),
                scopeValues, WebComponentUtil.getEnumChoiceRenderer(AbstractRoleMemberPanel.this),
                "abstractRoleMemberPanel.searchScope", "abstractRoleMemberPanel.searchScope.tooltip");
        searchScrope.add(new VisibleBehaviour(() -> getModelObject() instanceof OrgType));
        form.add(searchScrope);

        DropDownFormGroup<QName> typeSelect = createDropDown(ID_OBJECT_TYPE,
                Model.of(getMemberPanelStorage() != null ? getMemberPanelStorage().getType().getTypeQName()
                        : WebComponentUtil.classToQName(getPrismContext(), getDefaultObjectType())),
                getSupportedObjectTypes(true), new QNameObjectTypeChoiceRenderer(), "abstractRoleMemberPanel.type",
                "abstractRoleMemberPanel.type.tooltip");
        form.add(typeSelect);

        RelationDropDownChoicePanel relationSelector = new RelationDropDownChoicePanel(ID_SEARCH_BY_RELATION,
                getMemberPanelStorage() != null ? getMemberPanelStorage().getRelation() : null,
                getSupportedRelations(), true) {
            private static final long serialVersionUID = 1L;

            @Override
            protected void onValueChanged(AjaxRequestTarget target) {
                refreshAll(target);
            }
        };
        form.add(relationSelector);

        ChooseTypePanel<OrgType> tenant = createParameterPanel(ID_TENANT, true);
        form.add(tenant);
        tenant.add(new VisibleBehaviour(() -> getModelObject() instanceof RoleType));

        ChooseTypePanel<OrgType> project = createParameterPanel(ID_PROJECT, false);
        form.add(project);
        project.add(new VisibleBehaviour(() -> getModelObject() instanceof RoleType));

        CheckFormGroup includeIndirectMembers = new CheckFormGroup(ID_INDIRECT_MEMBERS,
                Model.of(getMemberPanelStorage() != null ? getMemberPanelStorage().getIndirect() : false),
                createStringResource("abstractRoleMemberPanel.indirectMembers"),
                "abstractRoleMemberPanel.indirectMembers.tooltip", false, "col-md-4", "col-md-2");
        includeIndirectMembers.getCheck().add(new AjaxFormComponentUpdatingBehavior("change") {

            private static final long serialVersionUID = 1L;

            protected void onUpdate(AjaxRequestTarget target) {
                refreshAll(target);
            };

        });

        includeIndirectMembers.getCheck().add(new EnableBehaviour(
                () -> getSearchScopeValue().equals(SearchBoxScopeType.ONE_LEVEL) || !searchScrope.isVisible()));
        includeIndirectMembers.setOutputMarkupId(true);
        form.add(includeIndirectMembers);

    }

    protected List<QName> getSupportedObjectTypes(boolean includeAbstractTypes) {
        return WebComponentUtil.createFocusTypeList(includeAbstractTypes);
    }

    private ChooseTypePanel<OrgType> createParameterPanel(String id, boolean isTenant) {

        ChooseTypePanel<OrgType> orgSelector = new ChooseTypePanel<OrgType>(id, Model.of(new ObjectViewDto())) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void executeCustomAction(AjaxRequestTarget target, OrgType object) {
                refreshAll(target);
            }

            @Override
            protected void executeCustomRemoveAction(AjaxRequestTarget target) {
                refreshAll(target);
            }

            @Override
            protected ObjectQuery getChooseQuery() {
                S_FilterEntryOrEmpty q = getPrismContext().queryFor(OrgType.class);
                if (isTenant) {
                    return q.item(OrgType.F_TENANT).eq(true).build();
                } else {
                    return q.not().item(OrgType.F_TENANT).eq(true).build();
                }
            }

            @Override
            protected boolean isSearchEnabled() {
                return true;
            }

            @Override
            public Class<OrgType> getObjectTypeClass() {
                return OrgType.class;
            }

            @Override
            protected AttributeAppender getInputStyleClass() {
                return AttributeAppender.append("class", "col-md-10");
            }

        };
        orgSelector.setOutputMarkupId(true);
        orgSelector.setOutputMarkupPlaceholderTag(true);
        return orgSelector;

    }

    private <V> DropDownFormGroup<V> createDropDown(String id, IModel<V> defaultModel, final List<V> values,
            IChoiceRenderer<V> renderer, String labelKey, String tooltipKey) {
        DropDownFormGroup<V> listSelect = new DropDownFormGroup<V>(id, defaultModel, Model.ofList(values), renderer,
                createStringResource(labelKey), tooltipKey, false, "col-md-4", "col-md-8", true);

        listSelect.getInput().add(new OnChangeAjaxBehavior() {
            private static final long serialVersionUID = 1L;

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                refreshAll(target);
            }
        });
        listSelect.setOutputMarkupId(true);
        return listSelect;
    }

    protected void refreshAll(AjaxRequestTarget target) {
        updateMembersPanelSessionStorage();

        DropDownFormGroup<QName> typeChoice = (DropDownFormGroup) get(createComponentPath(ID_FORM, ID_OBJECT_TYPE));
        QName type = getMemberPanelStorage() != null ? getMemberPanelStorage().getType().getTypeQName()
                : typeChoice.getModelObject();
        getMemberTable().clearCache();
        getMemberTable().refreshTable(WebComponentUtil.qnameToClass(getPrismContext(), type, FocusType.class),
                target);
        target.add(this);
    }

    private MainObjectListPanel<FocusType> getMemberTable() {
        return (MainObjectListPanel<FocusType>) get(
                createComponentPath(ID_FORM, ID_CONTAINER_MEMBER, ID_MEMBER_TABLE));
    }

    protected QueryScope getQueryScope(boolean isRecompute) {
        if (CollectionUtils
                .isNotEmpty(MemberOperationsHelper.getFocusOidToRecompute(getMemberTable().getSelectedObjects()))) {
            return QueryScope.SELECTED;
        }

        if (getIndirectmembersPanel().getValue()) {
            return QueryScope.ALL;
        }

        return QueryScope.ALL_DIRECT;
    }

    private CheckFormGroup getIndirectmembersPanel() {
        return (CheckFormGroup) get(createComponentPath(ID_FORM, ID_INDIRECT_MEMBERS));
    }

    protected void recomputeMembersPerformed(AjaxRequestTarget target) {
        MemberOperationsHelper.recomputeMembersPerformed(getPageBase(), getQueryScope(true),
                getActionQuery(getQueryScope(true), getSupportedRelations()), getSupportedRelations(), target);

    }

    protected ObjectQuery createContentQuery() {
        CheckFormGroup isIndirect = getIndirectmembersPanel();
        List<QName> relations = QNameUtil.match(getSelectedRelation(), PrismConstants.Q_ANY)
                ? getSupportedRelations()
                : Arrays.asList(getSelectedRelation());
        return createMemberQuery(isIndirect != null ? isIndirect.getValue() : false, relations);

    }

    protected QName getSelectedRelation() {
        MemberPanelStorage storage = getMemberPanelStorage();
        if (storage != null) {
            return storage.getRelation();
        }
        RelationDropDownChoicePanel relationDropDown = (RelationDropDownChoicePanel) get(
                createComponentPath(ID_FORM, ID_SEARCH_BY_RELATION));
        return relationDropDown.getRelationValue();
    }

    private SearchBoxScopeType getSearchScopeValue() {
        if (getMemberPanelStorage() != null) {
            return getMemberPanelStorage().getOrgSearchScope();
        }
        DropDownFormGroup<SearchBoxScopeType> searchScopeComponent = (DropDownFormGroup<SearchBoxScopeType>) get(
                createComponentPath(ID_FORM, ID_SEARCH_SCOPE));
        return searchScopeComponent.getModelObject();
    }

    protected ObjectTypes getSearchType() {
        DropDownFormGroup<QName> searchByTypeChoice = (DropDownFormGroup<QName>) get(
                createComponentPath(ID_FORM, ID_OBJECT_TYPE));
        QName typeName = searchByTypeChoice.getModelObject();
        return ObjectTypes.getObjectTypeFromTypeQName(typeName);
    }

    protected ObjectQuery createMemberQuery(boolean indirect, Collection<QName> relations) {
        if (indirect) {
            return createAllMemberQuery(relations);
        }

        return MemberOperationsHelper.createDirectMemberQuery(getModelObject(), getSearchType().getTypeQName(),
                relations, getParameter(ID_TENANT), getParameter(ID_PROJECT), getPrismContext());
    }

    protected ObjectQuery createAllMemberQuery(Collection<QName> relations) {
        return getPrismContext().queryFor(FocusType.class).item(FocusType.F_ROLE_MEMBERSHIP_REF)
                .ref(MemberOperationsHelper.createReferenceValuesList(getModelObject(), relations)).build();
    }

    protected ObjectReferenceType createReference() {
        ObjectReferenceType ref = ObjectTypeUtil.createObjectRef(getModelObject(), getPageBase().getPrismContext());
        return ref;
    }

    protected void detailsPerformed(AjaxRequestTarget target, ObjectType object) {
        if (WebComponentUtil.hasDetailsPage(object.getClass())) {
            WebComponentUtil.dispatchToObjectDetailsPage(object.getClass(), object.getOid(), this, true);
        } else {
            error("Could not find proper response page");
            throw new RestartResponseException(getPageBase());
        }
    }

    protected List<IColumn<SelectableBean<ObjectType>, String>> createMembersColumns() {
        List<IColumn<SelectableBean<ObjectType>, String>> columns = new ArrayList<>();

        IColumn<SelectableBean<ObjectType>, String> column = new AbstractExportableColumn<SelectableBean<ObjectType>, String>(
                createStringResource("TreeTablePanel.fullName.displayName")) {
            private static final long serialVersionUID = 1L;

            @Override
            public void populateItem(Item<ICellPopulator<SelectableBean<ObjectType>>> cellItem, String componentId,
                    IModel<SelectableBean<ObjectType>> rowModel) {
                SelectableBean<ObjectType> bean = rowModel.getObject();
                ObjectType object = bean.getValue();
                cellItem.add(new Label(componentId, getMemberObjectDisplayName(object)));
            }

            @Override
            public IModel<String> getDataModel(IModel<SelectableBean<ObjectType>> rowModel) {
                return Model.of(getMemberObjectDisplayName(rowModel.getObject().getValue()));
            }

        };
        columns.add(column);

        column = new AbstractExportableColumn<SelectableBean<ObjectType>, String>(
                createStringResource("TreeTablePanel.identifier.description")) {
            private static final long serialVersionUID = 1L;

            @Override
            public void populateItem(Item<ICellPopulator<SelectableBean<ObjectType>>> cellItem, String componentId,
                    IModel<SelectableBean<ObjectType>> rowModel) {
                SelectableBean<ObjectType> bean = rowModel.getObject();
                ObjectType object = bean.getValue();
                cellItem.add(new Label(componentId, getMemberObjectIdentifier(object)));
            }

            @Override
            public IModel<String> getDataModel(IModel<SelectableBean<ObjectType>> rowModel) {
                return Model.of(getMemberObjectIdentifier(rowModel.getObject().getValue()));
            }

        };
        columns.add(column);
        //      if (isRelationColumnVisible()){
        columns.add(createRelationColumn());
        //      }
        return columns;
    }

    protected IColumn<SelectableBean<ObjectType>, String> createRelationColumn() {
        return new AbstractExportableColumn<SelectableBean<ObjectType>, String>(
                createStringResource("roleMemberPanel.relation")) {
            private static final long serialVersionUID = 1L;

            @Override
            public void populateItem(Item<ICellPopulator<SelectableBean<ObjectType>>> cellItem, String componentId,
                    IModel<SelectableBean<ObjectType>> rowModel) {
                cellItem.add(new Label(componentId, getRelationValue(rowModel.getObject().getValue())));
            }

            @Override
            public IModel<String> getDataModel(IModel<SelectableBean<ObjectType>> rowModel) {
                return Model.of(getRelationValue(rowModel.getObject().getValue()));
            }

        };
    }

    protected boolean isRelationColumnVisible() {
        return false;
    }

    private String getMemberObjectDisplayName(ObjectType object) {
        if (object == null) {
            return "";
        }
        if (object instanceof UserType) {
            return WebComponentUtil.getOrigStringFromPoly(((UserType) object).getFullName());
        } else if (object instanceof AbstractRoleType) {
            return WebComponentUtil.getOrigStringFromPoly(((AbstractRoleType) object).getDisplayName());
        } else {
            return "";
        }
    }

    private String getMemberObjectIdentifier(ObjectType object) {
        if (object == null) {
            return "";
        }
        if (object instanceof UserType) {
            return ((UserType) object).getEmailAddress();
        } else if (object instanceof AbstractRoleType) {
            return ((AbstractRoleType) object).getIdentifier();
        } else {
            return object.getDescription();
        }
    }

    private Collection<SelectorOptions<GetOperationOptions>> getSearchOptions() {
        return SelectorOptions.createCollection(GetOperationOptions.createDistinct());
    }

    protected <O extends ObjectType> Class<O> getDefaultObjectType() {
        return (Class<O>) FocusType.class;
    }

    protected Form getFormComponent() {
        return (Form) get(ID_FORM);
    }

    private String getRelationValue(ObjectType focusObject) {
        String relation = "";
        if (FocusType.class.isAssignableFrom(focusObject.getClass())) {
            // Do NOT take relation from an assignment. Use roleMembershipRef instead. Reasons:
            // 1. Authorizations (MID-4893). User may be authorized just for roleMemberhsipRef and not for assignment
            //    Authorization for roleMembershipRef is enough to display member panel.
            // 2. There may be assignments that are not valid. We do not want to display relation for those.
            for (ObjectReferenceType roleMembershipRef : ((FocusType) focusObject).getRoleMembershipRef()) {
                relation = buildRelation(roleMembershipRef, relation);
            }

        }
        return relation;

    }

    private String buildRelation(ObjectReferenceType roleMembershipRef, String relation) {
        if (roleMembershipRef.getOid().equals(getModelObject().getOid())) {
            QName assignmentRelation = roleMembershipRef.getRelation();
            if (getSupportedRelations().stream().anyMatch(r -> QNameUtil.match(r, assignmentRelation))) {
                if (!StringUtils.isBlank(relation)) {
                    relation += ",";
                }
                relation += assignmentRelation.getLocalPart();
            }
        }
        return relation;
    }

    protected void updateMembersPanelSessionStorage() {
        MemberPanelStorage storage = getMemberPanelStorage();
        if (storage != null) {
            storage.setType(getSearchType());

            RelationDropDownChoicePanel relationDropDown = (RelationDropDownChoicePanel) get(
                    createComponentPath(ID_FORM, ID_SEARCH_BY_RELATION));
            storage.setRelation(relationDropDown.getRelationValue());

            CheckFormGroup indirectPanel = getIndirectmembersPanel();
            if (indirectPanel != null) {
                storage.setIndirect(indirectPanel.getValue());
            }

            DropDownFormGroup<SearchBoxScopeType> searchScopeComponent = (DropDownFormGroup<SearchBoxScopeType>) get(
                    createComponentPath(ID_FORM, ID_SEARCH_SCOPE));
            storage.setOrgSearchScope(searchScopeComponent.getModelObject());
        }
    }

    protected MemberPanelStorage getMemberPanelStorage() {
        return null;
    }
}