/*
* Copyright (c) 2015-2016 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.web.page.admin.roles;
import com.evolveum.midpoint.common.SystemConfigurationHolder;
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.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.query.InOidFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.TypeFilter;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.query.builder.S_AtomicFilterExit;
import com.evolveum.midpoint.prism.query.builder.S_FilterEntryOrEmpty;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskCategory;
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.assignment.RelationTypes;
import com.evolveum.midpoint.web.component.data.column.CheckBoxPanel;
import com.evolveum.midpoint.web.component.input.ObjectTypeChoiceRenderer;
import com.evolveum.midpoint.web.component.input.QNameChoiceRenderer;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem;
import com.evolveum.midpoint.web.component.util.VisibleBehaviour;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.admin.users.component.AbstractRoleMemberPanel;
import com.evolveum.midpoint.web.session.UserProfileStorage.TableId;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class RoleMemberPanel<T extends AbstractRoleType> extends AbstractRoleMemberPanel<T> {
private static final long serialVersionUID = 1L;
private static final Trace LOGGER = TraceManager.getTrace(RoleMemberPanel.class);
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";
public RoleMemberPanel(String id, IModel<T> model, PageBase pageBase) {
super(id, TableId.ROLE_MEMEBER_PANEL, model, pageBase);
}
public RoleMemberPanel(String id, IModel<T> model, List<RelationTypes> relations, PageBase pageBase) {
super(id, TableId.ROLE_MEMEBER_PANEL, model, relations, pageBase);
}
protected boolean isRole() {
return true;
}
private PrismContext getPrismContext() {
return getPageBase().getPrismContext();
}
private <V> DropDownChoice<V> createDropDown(String id, IModel<V> defaultModel, final List<V> values,
IChoiceRenderer<V> renderer) {
DropDownChoice<V> listSelect = new DropDownChoice<V>(id, defaultModel,
new AbstractReadOnlyModel<List<V>>() {
private static final long serialVersionUID = 1L;
@Override
public List<V> getObject() {
return values;
}
}, renderer);
listSelect.add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = 1L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
refreshTable(target);
}
});
return listSelect;
}
protected void refreshTable(AjaxRequestTarget target) {
DropDownChoice<QName> typeChoice = (DropDownChoice) get(createComponentPath(ID_OBJECT_TYPE));
QName type = typeChoice.getModelObject();
getMemberTable().clearCache();
getMemberTable().refreshTable(WebComponentUtil.qnameToClass(getPrismContext(), type, FocusType.class), target);
}
private List<OrgType> createTenantList() {
ObjectQuery query = QueryBuilder.queryFor(OrgType.class, getPrismContext())
.item(OrgType.F_TENANT).eq(true)
.build();
List<PrismObject<OrgType>> orgs = WebModelServiceUtils.searchObjects(OrgType.class, query,
new OperationResult("Tenant search"), getPageBase());
List<OrgType> orgTypes = new ArrayList<>();
for (PrismObject<OrgType> org : orgs) {
orgTypes.add(org.asObjectable());
}
return orgTypes;
}
private List<OrgType> createProjectList() {
ObjectQuery query = QueryBuilder.queryFor(OrgType.class, getPrismContext())
.item(OrgType.F_TENANT).eq(true)
.or().item(OrgType.F_TENANT).isNull()
.build();
List<PrismObject<OrgType>> orgs = WebModelServiceUtils.searchObjects(OrgType.class, query,
new OperationResult("Tenant search"), getPageBase());
List<OrgType> orgTypes = new ArrayList<>();
for (PrismObject<OrgType> org : orgs) {
orgTypes.add(org.asObjectable());
}
return orgTypes;
}
private MainObjectListPanel<FocusType> getMemberTable() {
return (MainObjectListPanel<FocusType>) get(createComponentPath(ID_FORM, ID_CONTAINER_MEMBER, ID_MEMBER_TABLE));
}
private AssignmentType createAssignmentToModify() throws SchemaException {
AssignmentType assignmentToModify = createAssignmentToModify(null);
DropDownChoice<OrgType> tenantChoice = (DropDownChoice<OrgType>) get(ID_TENANT);
OrgType tenant = tenantChoice.getModelObject();
if (tenant != null) {
assignmentToModify.setTenantRef(ObjectTypeUtil.createObjectRef(tenant.getOid(), ObjectTypes.ORG));
}
DropDownChoice<OrgType> projectChoice = (DropDownChoice<OrgType>) get(ID_PROJECT);
OrgType project = projectChoice.getModelObject();
if (project != null) {
assignmentToModify.setOrgRef(ObjectTypeUtil.createObjectRef(project.getOid(), ObjectTypes.ORG));
}
return assignmentToModify;
}
private ObjectQuery getActionQuery(QueryScope scope) {
switch (scope) {
case ALL:
return createAllMemberQuery();
case ALL_DIRECT:
return createDirectMemberQuery( );
case SELECTED:
return createRecomputeQuery();
}
return null;
}
private ObjectQuery createAllMemberQuery() {
return QueryBuilder.queryFor(FocusType.class, getPrismContext())
.item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF)
.ref(createReferenceValuesList()).build();
}
private List<PrismReferenceValue> createReferenceValuesList() {
List<PrismReferenceValue> referenceValuesList = new ArrayList<>();
if (relations != null && relations.size() > 0){
for (RelationTypes relation : relations) {
PrismReferenceValue rv = new PrismReferenceValue(getModelObject().getOid());
rv.setRelation(relation.getRelation());
referenceValuesList.add(rv);
}
} else {
PrismReferenceValue rv = new PrismReferenceValue(getModelObject().getOid());
referenceValuesList.add(rv);
}
return referenceValuesList;
}
private ObjectQuery createRecomputeQuery() {
Set<String> oids = getFocusOidToRecompute();
ObjectQuery query = ObjectQuery.createObjectQuery(InOidFilter.createInOid(oids));
return query;
}
private Set<String> getFocusOidToRecompute() {
List<FocusType> availableData = getMemberTable().getSelectedObjects();
Set<String> oids = new HashSet<>();
for (FocusType focus : availableData) {
oids.add(focus.getOid());
}
return oids;
}
@Override
protected void initCustomLayout(Form form) {
}
@Override
protected void initSearch(Form form) {
List<QName> allowedTypes = WebComponentUtil.createFocusTypeList();
allowedTypes.add(FocusType.COMPLEX_TYPE);
DropDownChoice<QName> typeSelect = createDropDown(ID_OBJECT_TYPE, Model.of(FocusType.COMPLEX_TYPE),
allowedTypes, new QNameChoiceRenderer());
add(typeSelect);
DropDownChoice<OrgType> tenant = createDropDown(ID_TENANT, new Model(),
createTenantList(), new ObjectTypeChoiceRenderer<OrgType>());
add(tenant);
tenant.add(new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return isRole();
}
});
DropDownChoice<OrgType> project = createDropDown(ID_PROJECT, new Model(),
createProjectList(), new ObjectTypeChoiceRenderer<OrgType>());
add(project);
project.add(new VisibleEnableBehaviour() {
@Override
public boolean isVisible() {
return isRole();
}
});
CheckBoxPanel includeIndirectMembers = new CheckBoxPanel(ID_INDIRECT_MEMBERS, new Model<Boolean>(false)) {
private static final long serialVersionUID = 1L;
public void onUpdate(AjaxRequestTarget target) {
refreshTable(target);
}
};
add(includeIndirectMembers);
includeIndirectMembers.add(new VisibleBehaviour(this::isRole)); // TODO shouldn't we hide also the label?
}
@Override
protected void addMembersPerformed(QName type, QName relation, List selected, AjaxRequestTarget target) {
Task operationalTask = getPageBase().createSimpleTask(getTaskName("Add", null));
ObjectDelta delta = prepareDelta(type, MemberOperation.ADD, operationalTask.getResult());
executeMemberOperation(operationalTask, type, createQueryForAdd(selected), delta,
TaskCategory.EXECUTE_CHANGES, target);
}
private ObjectDelta prepareDelta(QName type, MemberOperation operation, OperationResult result) {
Class classType = WebComponentUtil.qnameToClass(getPrismContext(), type);
ObjectDelta delta = null;
try {
switch (operation) {
case ADD:
delta = ObjectDelta.createModificationAddContainer(classType, "fakeOid",
FocusType.F_ASSIGNMENT, getPrismContext(), createAssignmentToModify());
break;
case REMOVE:
delta = ObjectDelta.createModificationDeleteContainer(classType, "fakeOid",
FocusType.F_ASSIGNMENT, getPrismContext(), createAssignmentToModify());
break;
}
} catch (SchemaException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Failed to prepare delta for operation " + operation.name(), e);
result.recordFatalError("Failed to prepare delta for operation " + operation.name(), e);
}
return delta;
}
@Override
protected void removeMembersPerformed(QueryScope scope, AjaxRequestTarget target) {
Task operationalTask = getPageBase().createSimpleTask(getTaskName("Remove", scope));
ObjectDelta delta = prepareDelta(FocusType.COMPLEX_TYPE, MemberOperation.REMOVE, operationalTask.getResult());
executeMemberOperation(operationalTask, FocusType.COMPLEX_TYPE, getActionQuery(scope), delta,
TaskCategory.EXECUTE_CHANGES, target);
}
@Override
protected void recomputeMembersPerformed(QueryScope scope, AjaxRequestTarget target) {
Task operationalTask = getPageBase().createSimpleTask(getTaskName("Recompute", scope));
executeMemberOperation(operationalTask, FocusType.COMPLEX_TYPE, getActionQuery(scope), null,
TaskCategory.RECOMPUTATION, target);
}
@Override
protected ObjectQuery createContentQuery() {
boolean indirect = ((CheckBoxPanel) get(createComponentPath(ID_INDIRECT_MEMBERS))).getValue();
return indirect ? createAllMemberQuery() : createDirectMemberQuery();
}
private ObjectQuery createDirectMemberQuery() {
ObjectQuery query;
String oid = getModelObject().getOid();
S_AtomicFilterExit q = QueryBuilder.queryFor(FocusType.class, getPrismContext())
.item(FocusType.F_ASSIGNMENT, AssignmentType.F_TARGET_REF)
.ref(createReferenceValuesList());
DropDownChoice<OrgType> tenantChoice = (DropDownChoice) get(createComponentPath(ID_TENANT));
OrgType tenant = tenantChoice.getModelObject();
if (tenant != null) {
q = q.and().item(FocusType.F_ASSIGNMENT, AssignmentType.F_TENANT_REF).ref(createReference(tenant).asReferenceValue());
}
DropDownChoice<OrgType> projectChoice = (DropDownChoice) get(createComponentPath(ID_PROJECT));
OrgType project = projectChoice.getModelObject();
if (project != null) {
q = q.and().item(FocusType.F_ASSIGNMENT, AssignmentType.F_ORG_REF).ref(createReference(project).asReferenceValue());
}
query = q.build();
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Searching members of role {} with query:\n{}", oid, query.debugDump());
}
DropDownChoice<QName> objectTypeChoice = (DropDownChoice) get(createComponentPath(ID_OBJECT_TYPE));
QName objectType = objectTypeChoice.getModelObject();
if (objectType == null || FocusType.COMPLEX_TYPE.equals(objectType)) {
return query;
} else {
return ObjectQuery.createObjectQuery(TypeFilter.createType(objectType, query.getFilter()));
}
}
@Override
protected List<InlineMenuItem> createNewMemberInlineMenuItems() {
return super.createNewMemberInlineMenuItems();
}
@Override
protected List<InlineMenuItem> createRemoveMemberInlineMenuItems() {
return super.createRemoveMemberInlineMenuItems();
}
@Override
protected List<InlineMenuItem> createMemberRecomputeInlineMenuItems() {
return super.createMemberRecomputeInlineMenuItems();
}
}