package com.evolveum.midpoint.web.page.admin.users.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.model.api.ModelExecuteOptions;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.task.api.TaskCategory;
import com.evolveum.midpoint.web.component.assignment.RelationTypes;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.ajax.AjaxRequestTarget;
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.repeater.Item;
import org.apache.wicket.model.IModel;
import com.evolveum.midpoint.gui.api.component.BasePanel;
import com.evolveum.midpoint.gui.api.component.MainObjectListPanel;
import com.evolveum.midpoint.gui.api.component.ObjectBrowserPanel;
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.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.query.AndFilter;
import com.evolveum.midpoint.prism.query.InOidFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
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.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SystemException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.data.column.InlineMenuHeaderColumn;
import com.evolveum.midpoint.web.component.dialog.ChooseFocusTypeDialogPanel;
import com.evolveum.midpoint.web.component.menu.cog.InlineMenuItem;
import com.evolveum.midpoint.web.component.util.SelectableBean;
import com.evolveum.midpoint.web.page.admin.configuration.component.HeaderMenuAction;
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.NodeType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import org.apache.wicket.model.Model;
public abstract class AbstractRoleMemberPanel<T extends AbstractRoleType> extends BasePanel<T> {
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);
protected static final String ID_FORM = "form";
protected static final String ID_CONTAINER_MANAGER = "managerContainer";
protected static final String ID_CONTAINER_MEMBER = "memberContainer";
protected static final String ID_CHILD_TABLE = "childUnitTable";
protected static final String ID_MANAGER_TABLE = "managerTable";
protected static final String ID_MEMBER_TABLE = "memberTable";
protected List<RelationTypes> relations = new ArrayList<>();
public AbstractRoleMemberPanel(String id, TableId tableId, IModel<T> model, PageBase parentPage) {
super(id, model);
setParent(parentPage);
initLayout(tableId);
}
public AbstractRoleMemberPanel(String id, TableId tableId, IModel<T> model,
List<RelationTypes> relations, PageBase parentPage) {
super(id, model);
this.relations = relations;
setParent(parentPage);
initLayout(tableId);
}
private void initLayout(TableId tableId) {
Form form = new Form(ID_FORM);
form.setOutputMarkupId(true);
add(form);
initSearch(form);
initMemberTable(tableId, form);
initCustomLayout(form);
}
protected abstract void initCustomLayout(Form form);
protected abstract void initSearch(Form form);
private void initMemberTable(TableId tableId, Form form) {
WebMarkupContainer memberContainer = new WebMarkupContainer(ID_CONTAINER_MEMBER);
memberContainer.setOutputMarkupId(true);
memberContainer.setOutputMarkupPlaceholderTag(true);
form.add(memberContainer);
MainObjectListPanel<ObjectType> childrenListPanel = new MainObjectListPanel<ObjectType>(
ID_MEMBER_TABLE, ObjectType.class, tableId, getSearchOptions(), getPageBase()) {
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(null, target);
}
@Override
protected List<IColumn<SelectableBean<ObjectType>, String>> createColumns() {
return createMembersColumns();
}
@Override
protected IColumn<SelectableBean<ObjectType>, String> createActionsColumn(){
return new InlineMenuHeaderColumn(createMembersHeaderInlineMenu());
}
@Override
protected List<InlineMenuItem> createInlineMenu() {
return new ArrayList<>();
}
@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());
}
if (filters.size() == 1) {
return ObjectQuery.createObjectQuery(filters.iterator().next());
}
return ObjectQuery.createObjectQuery(AndFilter.createAnd(filters));
}
};
childrenListPanel.setOutputMarkupId(true);
memberContainer.add(childrenListPanel);
}
protected List<InlineMenuItem> createMembersHeaderInlineMenu() {
List<InlineMenuItem> headerMenuItems = new ArrayList<>();
headerMenuItems.addAll(createNewMemberInlineMenuItems());
headerMenuItems.add(new InlineMenuItem());
headerMenuItems.addAll(createRemoveMemberInlineMenuItems());
headerMenuItems.addAll(createMemberRecomputeInlineMenuItems());
return headerMenuItems;
}
protected List<InlineMenuItem> createNewMemberInlineMenuItems() {
List<InlineMenuItem> newMemberMenuItems = new ArrayList<>();
newMemberMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.createMember"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
createFocusMemberPerformed(null, target);
}
}));
newMemberMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.addMembers"), false,
new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
addMembers(null, target);
}
}));
return newMemberMenuItems;
}
protected List<InlineMenuItem> createMemberRecomputeInlineMenuItems() {
List<InlineMenuItem> recomputeMenuItems = new ArrayList<>();
recomputeMenuItems
.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.recomputeMembersSelected"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
recomputeMembersPerformed(QueryScope.SELECTED, target);
}
}));
recomputeMenuItems
.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.recomputeMembersAllDirect"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
recomputeMembersPerformed(QueryScope.ALL_DIRECT, target);
}
}));
recomputeMenuItems
.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.recomputeMembersAll"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
recomputeMembersPerformed(QueryScope.ALL, target);
}
}));
return recomputeMenuItems;
}
protected List<InlineMenuItem> createRemoveMemberInlineMenuItems() {
List<InlineMenuItem> removeMenuItems = new ArrayList<>();
removeMenuItems
.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.unassignMembersSelected"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
removeMembersPerformed(QueryScope.SELECTED, target);
}
}));
removeMenuItems.add(new InlineMenuItem(createStringResource("TreeTablePanel.menu.unassignMembersAll"),
false, new HeaderMenuAction(this) {
private static final long serialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
removeMembersPerformed(QueryScope.ALL, target);
}
}));
return removeMenuItems;
}
protected void createFocusMemberPerformed(final QName relation, AjaxRequestTarget target) {
ChooseFocusTypeDialogPanel chooseTypePopupContent = new ChooseFocusTypeDialogPanel(
getPageBase().getMainPopupBodyId()) {
private static final long serialVersionUID = 1L;
protected void okPerformed(QName type, AjaxRequestTarget target) {
try {
initObjectForAdd(null, type, relation, target);
} catch (SchemaException e) {
throw new SystemException(e.getMessage(), e);
}
};
};
getPageBase().showMainPopup(chooseTypePopupContent, target);
}
// TODO: merge this with TreeTablePanel.initObjectForAdd, also see MID-3233
private void initObjectForAdd(ObjectReferenceType parentOrgRef, QName type, QName relation,
AjaxRequestTarget target) throws SchemaException {
getPageBase().hideMainPopup(target);
PrismContext prismContext = getPageBase().getPrismContext();
PrismObjectDefinition def = prismContext.getSchemaRegistry().findObjectDefinitionByType(type);
PrismObject obj = def.instantiate();
if (parentOrgRef == null) {
parentOrgRef = createReference(relation);
}
ObjectType objType = (ObjectType) obj.asObjectable();
if (FocusType.class.isAssignableFrom(obj.getCompileTimeClass())) {
AssignmentType assignment = new AssignmentType();
assignment.setTargetRef(parentOrgRef);
((FocusType) objType).getAssignment().add(assignment);
}
// Set parentOrgRef in any case. This is not strictly correct.
// The parentOrgRef should be added by the projector. But
// this is needed to successfully pass through security
// TODO: fix MID-3234
if (parentOrgRef.getType() != null && OrgType.COMPLEX_TYPE.equals(parentOrgRef.getType())) {
objType.getParentOrgRef().add(parentOrgRef.clone());
}
WebComponentUtil.dispatchToObjectDetailsPage(obj, this);
}
protected void addMembers(final QName relation, AjaxRequestTarget target) {
List<QName> types = WebComponentUtil.createObjectTypeList();
types.remove(NodeType.COMPLEX_TYPE);
types.remove(ShadowType.COMPLEX_TYPE);
ObjectBrowserPanel<ObjectType> browser = new ObjectBrowserPanel(getPageBase().getMainPopupBodyId(),
UserType.class, types, true, getPageBase()) {
private static final long serialVersionUID = 1L;
@Override
protected void addPerformed(AjaxRequestTarget target, QName type, List selected) {
AbstractRoleMemberPanel.this.getPageBase().hideMainPopup(target);
AbstractRoleMemberPanel.this.addMembersPerformed(type, relation, selected, target);
}
};
browser.setOutputMarkupId(true);
getPageBase().showMainPopup(browser, target);
}
protected ObjectQuery createQueryForAdd(List selected) {
List<String> oids = new ArrayList<>();
for (Object selectable : selected) {
if (selectable instanceof ObjectType) {
oids.add(((ObjectType) selectable).getOid());
}
}
return ObjectQuery.createObjectQuery(InOidFilter.createInOid(oids));
}
protected abstract void addMembersPerformed(QName type, QName relation, List selected,
AjaxRequestTarget target);
protected abstract void removeMembersPerformed(QueryScope scope, AjaxRequestTarget target);
protected abstract void recomputeMembersPerformed(QueryScope scope, AjaxRequestTarget target);
protected void executeMemberOperation(Task operationalTask, QName type, ObjectQuery memberQuery,
ObjectDelta delta, String category, AjaxRequestTarget target) {
OperationResult parentResult = operationalTask.getResult();
try {
ModelExecuteOptions options = TaskCategory.EXECUTE_CHANGES.equals(category)
? ModelExecuteOptions.createReconcile() // This was originally in ExecuteChangesTaskHandler, now it's transferred through task extension.
: null;
TaskType task = WebComponentUtil.createSingleRecurrenceTask(parentResult.getOperation(), type,
memberQuery, delta, options, category, getPageBase());
WebModelServiceUtils.runTask(task, operationalTask, parentResult, getPageBase());
} catch (SchemaException e) {
parentResult.recordFatalError(parentResult.getOperation(), e);
LoggingUtils.logUnexpectedException(LOGGER,
"Failed to execute operation " + parentResult.getOperation(), e);
target.add(getPageBase().getFeedbackPanel());
}
target.add(getPageBase().getFeedbackPanel());
}
protected AssignmentType createAssignmentToModify(QName relation) throws SchemaException {
AssignmentType assignmentToModify = new AssignmentType();
assignmentToModify.setTargetRef(createReference(relation));
getPageBase().getPrismContext().adopt(assignmentToModify);
return assignmentToModify;
}
protected ObjectReferenceType createReference(QName relation) {
ObjectReferenceType ref = ObjectTypeUtil.createObjectRef(getModelObject());
ref.setRelation(relation);
return ref;
}
protected ObjectReferenceType createReference() {
ObjectReferenceType ref = ObjectTypeUtil.createObjectRef(getModelObject());
return ref;
}
protected ObjectReferenceType createReference(ObjectType obj) {
ObjectReferenceType ref = ObjectTypeUtil.createObjectRef(obj);
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);
return columns;
}
protected abstract ObjectQuery createContentQuery();
protected String getTaskName(String operation, QueryScope scope, boolean managers) {
StringBuilder nameBuilder = new StringBuilder(operation);
nameBuilder.append(" ");
if (scope != null) {
nameBuilder.append(scope.name());
nameBuilder.append(" ");
}
if (managers) {
nameBuilder.append("managers: ");
} else {
nameBuilder.append("members: ");
}
nameBuilder
.append(WebComponentUtil.getEffectiveName(getModelObject(), AbstractRoleType.F_DISPLAY_NAME));
return nameBuilder.toString();
}
protected String getTaskName(String operation, QueryScope scope) {
return getTaskName(operation, scope, 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();
}
}
protected Collection<SelectorOptions<GetOperationOptions>> getSearchOptions(){
return null;
}
}