/*
* Copyright (c) 2010-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.component.assignment;
import com.evolveum.midpoint.gui.api.component.BasePanel;
import com.evolveum.midpoint.gui.api.component.ObjectBrowserPanel;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.page.PageBase;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.model.api.ModelInteractionService;
import com.evolveum.midpoint.model.api.RoleSelectionSpecification;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
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.BaseSortableDataProvider;
import com.evolveum.midpoint.web.component.form.Form;
import com.evolveum.midpoint.web.component.form.multivalue.GenericMultiValueLabelEditPanel;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.admin.dto.ObjectViewDto;
import com.evolveum.midpoint.web.page.admin.users.dto.UserDtoStatus;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.model.IModel;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;
/**
* Panel contains available list of focus type items and
* the list of assigned items of the same type with the
* possibility of editing the list of assignments.
*
* @author Kate Honchar
*/
public class MultipleAssignmentSelectorPanel<F extends FocusType, H extends FocusType, G extends FocusType>
extends BasePanel<List<AssignmentEditorDto>> { //G - type of the object which is to be assigned (a.g. assign a role (RoleType))
//F - type of the focus which is opened for editing (e.g. edit user - UserType)
//H - a type of filter object (e.g. Filter by user - UserType)
private static final long serialVersionUID = 1L;
private LoadableModel<List<AssignmentEditorDto>> assignmentsModel;
private static final String ID_BUTTON_REMOVE = "remove";
private static final String ID_BUTTON_ADD = "add";
private static final String ID_FORM = "form";
private static final String ID_AVAILABLE_ASSIGNMENTS = "availableAssignments";
private static final String ID_CURRENT_ASSIGNMENTS = "currentAssignments";
private static final String ID_TENANT_EDITOR = "tenantEditor";
private static final String ID_ORG_EDITOR = "orgEditor";
private static final String ID_BUTTON_RESET = "buttonReset";
private static final String LABEL_SIZE = "col-md-4";
private static final String INPUT_SIZE = "col-md-10";
private static final String DOT_CLASS = MultipleAssignmentSelectorPanel.class.getName();
private Class<G> type;
private List<OrgType> tenantEditorObject = new ArrayList<>();
private List<OrgType> orgEditorObject = new ArrayList<>();
private PrismObject<F> focus;
private static final Trace LOGGER = TraceManager.getTrace(MultipleAssignmentSelectorPanel.class);
private static final String OPERATION_LOAD_ASSIGNABLE_ROLES = DOT_CLASS + "loadAssignableRoles";
public MultipleAssignmentSelectorPanel(String id, LoadableModel<List<AssignmentEditorDto>> assignmentsModel,
PrismObject<F> focus, Class<H> targetFocusClass, Class<G> type, PageBase page) {
super(id, assignmentsModel);
this.assignmentsModel = assignmentsModel;
this.type = type;
this.focus = focus;
tenantEditorObject.add(new OrgType());
orgEditorObject.add(new OrgType());
initLayout(targetFocusClass, page);
}
private void initLayout(Class<H> targetFocusClass, PageBase page) {
IModel<List<AssignmentEditorDto>> availableAssignmentModel = createAvailableAssignmentModel();
final MultipleAssignmentSelector availableAssignmentsPanel = new MultipleAssignmentSelector<F, H>(ID_AVAILABLE_ASSIGNMENTS,
availableAssignmentModel, targetFocusClass, type, focus, getFilterModel(true), page);
final MultipleAssignmentSelector currentAssignmentsPanel = new MultipleAssignmentSelector<F, H>(ID_CURRENT_ASSIGNMENTS,
assignmentsModel, targetFocusClass, type, null, getFilterModel(true), page){
@Override
protected List<AssignmentEditorDto> getListProviderDataList(){
return assignmentsModel.getObject();
}
};
currentAssignmentsPanel.setFilterButtonVisibility(false);
AjaxButton add = new AjaxButton(ID_BUTTON_ADD) {
@Override
protected void onSubmit(AjaxRequestTarget target, org.apache.wicket.markup.html.form.Form form) {
addToAssignmentsModel(target, availableAssignmentsPanel, currentAssignmentsPanel);
}
};
AjaxButton remove = new AjaxButton(ID_BUTTON_REMOVE) {
@Override
protected void onSubmit(AjaxRequestTarget target, org.apache.wicket.markup.html.form.Form form) {
deleteFromAssignmentsModel(target, currentAssignmentsPanel, availableAssignmentsPanel);
}
};
remove.add(new VisibleEnableBehaviour() {
@Override
public boolean isEnabled() {
return WebComponentUtil.isAuthorized(AuthorizationConstants.AUTZ_UI_UNASSIGN_ACTION_URL);
}
});
AjaxLink<String> buttonReset = new AjaxLink<String>(ID_BUTTON_RESET) {
@Override
public void onClick(AjaxRequestTarget target) {
reset(currentAssignmentsPanel);
target.add(currentAssignmentsPanel);
}
};
buttonReset.setBody(createStringResource("MultipleAssignmentSelector.reset"));
Form<?> form = new Form<Void>(ID_FORM);
form.add(createTenantContainer());
form.add(createOrgContainer());
form.add(availableAssignmentsPanel);
form.add(currentAssignmentsPanel);
form.add(buttonReset);
form.add(add);
form.add(remove);
add(form);
}
private IModel<List<AssignmentEditorDto>> createAvailableAssignmentModel() {
return new IModel<List<AssignmentEditorDto>>() {
@Override
public List<AssignmentEditorDto> getObject() {
return new ArrayList<>();
}
@Override
public void setObject(List<AssignmentEditorDto> assignmentList) {
}
@Override
public void detach() {
}
};
}
private void addToAssignmentsModel(AjaxRequestTarget target, MultipleAssignmentSelector from, MultipleAssignmentSelector to) {
List<AssignmentEditorDto> fromProviderList = from.getProvider().getAvailableData();
List<AssignmentEditorDto> listToBeAdded = new ArrayList<>();
List<AssignmentEditorDto> assignmentsList = assignmentsModel.getObject();
if (tenantEditorObject != null && StringUtils.isNotEmpty(tenantEditorObject.get(0).getOid()) ||
orgEditorObject != null && StringUtils.isNotEmpty(orgEditorObject.get(0).getOid())) {
setTenantAndOrgToAssignmentsList(fromProviderList);
}
for (AssignmentEditorDto dto : fromProviderList) {
if (dto.isSelected()) {
boolean toBeAdded = true;
for (AssignmentEditorDto assignmentDto : assignmentsList) {
String assignmentOid = getAssignmentDtoOid(assignmentDto);
String dtoOid = getAssignmentDtoOid(dto);
if (assignmentOid != null &&
dtoOid != null && assignmentOid.equals(dtoOid)) {
if (areEqualReferenceObjects(assignmentDto.getTenantRef(), dto.getTenantRef()) &&
areEqualReferenceObjects(assignmentDto.getOrgRef(), dto.getOrgRef())){
if (assignmentDto.getStatus().equals(UserDtoStatus.DELETE)) {
assignmentDto.setStatus(UserDtoStatus.MODIFY);
}
assignmentDto.setTenantRef(dto.getTenantRef());
assignmentDto.setOrgRef(dto.getOrgRef());
toBeAdded = false;
}
}
}
if (toBeAdded) {
dto.setStatus(UserDtoStatus.ADD);
listToBeAdded.add(dto);
}
dto.setSelected(false);
}
}
assignmentsList.addAll(listToBeAdded);
target.add(to);
target.add(from);
}
private void deleteFromAssignmentsModel(AjaxRequestTarget target, MultipleAssignmentSelector from, MultipleAssignmentSelector to) {
List<AssignmentEditorDto> fromProviderList = ((BaseSortableDataProvider) from.getProvider()).getAvailableData();
List<AssignmentEditorDto> listToBeRemoved = new ArrayList<>();
List<AssignmentEditorDto> assignmentsList = assignmentsModel.getObject();
for (AssignmentEditorDto dto : fromProviderList) {
if (dto.isSelected()) {
for (AssignmentEditorDto assignmentDto : assignmentsList) {
String assignmentDtoOid = getAssignmentDtoOid(assignmentDto);
String dtoOid = getAssignmentDtoOid(dto);
if (assignmentDtoOid != null &&
dtoOid != null && assignmentDtoOid.equals(dtoOid) &&
areEqualReferenceObjects(assignmentDto.getTenantRef(), dto.getTenantRef()) &&
areEqualReferenceObjects(assignmentDto.getOrgRef(), dto.getOrgRef())) {
if (assignmentDto.getStatus().equals(UserDtoStatus.ADD)) {
listToBeRemoved.add(assignmentDto);
} else {
assignmentDto.setStatus(UserDtoStatus.DELETE);
}
}
}
dto.setSelected(false);
}
}
assignmentsList.removeAll(listToBeRemoved);
target.add(to);
target.add(from);
}
private IModel<ObjectFilter> getFilterModel(final boolean isRequestableFilter){
return new IModel<ObjectFilter>() {
@Override
public ObjectFilter getObject() {
ObjectFilter archivedRolesFilter = QueryBuilder.queryFor(RoleType.class, getPageBase().getPrismContext())
.item(RoleType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS).eq(ActivationStatusType.ARCHIVED)
.buildFilter();
ObjectFilter filter;
if (isRequestableFilter) {
ObjectFilter assignableRolesFilter = getAssignableRolesFilter();
if (assignableRolesFilter instanceof NotFilter) {
return null;
} else if (assignableRolesFilter != null) {
filter = AndFilter.createAnd(assignableRolesFilter, new NotFilter(archivedRolesFilter));
} else {
filter = new NotFilter(archivedRolesFilter);
}
return filter;
} else {
return new NotFilter(archivedRolesFilter);
}
}
@Override
public void setObject(ObjectFilter objectFilter) {
}
@Override
public void detach() {
}
};
}
private ObjectFilter getAssignableRolesFilter() {
LOGGER.debug("Loading roles which the current user has right to assign");
OperationResult result = new OperationResult(OPERATION_LOAD_ASSIGNABLE_ROLES);
ObjectFilter filter = null;
try {
PageBase pb = getPageBase();
ModelInteractionService mis = pb.getModelInteractionService();
RoleSelectionSpecification roleSpec = mis.getAssignableRoleSpecification(focus, result);
filter = roleSpec.getFilter();
} catch (Exception ex) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't load available roles", ex);
result.recordFatalError("Couldn't load available roles", ex);
} finally {
result.recomputeStatus();
}
if (!result.isSuccess() && !result.isHandledError()) {
getPageBase().showResult(result);
}
return filter;
}
private GenericMultiValueLabelEditPanel createTenantContainer(){
final GenericMultiValueLabelEditPanel tenantEditor = new GenericMultiValueLabelEditPanel<OrgType>(ID_TENANT_EDITOR,
createTenantModel(),
createStringResource("MultipleAssignmentSelector.tenant"), LABEL_SIZE, INPUT_SIZE, false){
@Override
protected boolean getLabelVisibility(){
return false;
}
@Override
protected IModel<String> createTextModel(final IModel<OrgType> model) {
return new IModel<String>() {
@Override
public String getObject() {
return WebComponentUtil.getName(model.getObject().asPrismObject());
}
@Override
public void setObject(String s) {
}
@Override
public void detach() {
}
};
}
@Override
protected void removeValuePerformed(AjaxRequestTarget target, ListItem<OrgType> item) {
tenantEditorObject.clear();
tenantEditorObject.add(new OrgType());
target.add(getTenantEditorContainer());
}
@Override
protected void editValuePerformed(AjaxRequestTarget target, IModel<OrgType> rowModel) {
List<QName> supportedTypes = new ArrayList<>();
supportedTypes.add(getPageBase().getPrismContext().getSchemaRegistry()
.findObjectDefinitionByCompileTimeClass(OrgType.class).getTypeName());
ObjectFilter filter = QueryBuilder.queryFor(OrgType.class, getPageBase().getPrismContext())
.item(OrgType.F_TENANT).eq(true)
.buildFilter();
ObjectBrowserPanel<OrgType> tenantPanel = new ObjectBrowserPanel<OrgType>(getPageBase().getMainPopupBodyId(),
OrgType.class, supportedTypes, false, getPageBase(), filter) {
@Override
protected void onSelectPerformed(AjaxRequestTarget target, OrgType org) {
super.onSelectPerformed(target, org);
tenantEditorObject.clear();
tenantEditorObject.add(org);
target.add(getTenantEditorContainer());
}
};
// OrgTreeAssignablePanel tenantPanel = new OrgTreeAssignablePanel(
// getPageBase().getMainPopupBodyId(), false, getPageBase()) {
//
// @Override
// protected void onItemSelect(SelectableBean<OrgType> selected, AjaxRequestTarget target) {
// closeModalWindow(target);
// tenantEditorObject.clear();
// tenantEditorObject.add(selected.getValue());
// target.add(getTenantEditorContainer()); }
// };
getPageBase().showMainPopup(tenantPanel, target);
}
// @Override
// protected void setDialogSize() {
// getPageBase().getMainPopup().setInitialWidth(900);
// getPageBase().getMainPopup().setInitialHeight(700);
// }
};
tenantEditor.setOutputMarkupId(true);
return tenantEditor;
}
private GenericMultiValueLabelEditPanel createOrgContainer(){
final GenericMultiValueLabelEditPanel orgUnitEditor = new GenericMultiValueLabelEditPanel<OrgType>(ID_ORG_EDITOR,
createOrgUnitModel(),
createStringResource("MultipleAssignmentSelector.orgUnit"), LABEL_SIZE, INPUT_SIZE, false){
@Override
protected boolean getLabelVisibility(){
return false;
}
@Override
protected IModel<String> createTextModel(final IModel<OrgType> model) {
return new IModel<String>() {
@Override
public String getObject() {
return WebComponentUtil.getName(model.getObject().asPrismObject());
}
@Override
public void setObject(String s) {
}
@Override
public void detach() {
}
};
}
@Override
protected void removeValuePerformed(AjaxRequestTarget target, ListItem<OrgType> item) {
orgEditorObject.clear();
orgEditorObject.add(new OrgType());
target.add(getOrgUnitEditorContainer());
}
@Override
protected void editValuePerformed(AjaxRequestTarget target, IModel<OrgType> rowModel) {
List<QName> supportedTypes = new ArrayList<>();
supportedTypes.add(getPageBase().getPrismContext().getSchemaRegistry()
.findObjectDefinitionByCompileTimeClass(OrgType.class).getTypeName());
ObjectBrowserPanel<OrgType> orgPanel = new ObjectBrowserPanel<OrgType>(getPageBase().getMainPopupBodyId(),
OrgType.class, supportedTypes, false, getPageBase()) {
@Override
protected void onSelectPerformed(AjaxRequestTarget target, OrgType org) {
super.onSelectPerformed(target, org);
orgEditorObject.clear();
orgEditorObject.add(org);
target.add(getOrgUnitEditorContainer());
}
};
getPageBase().showMainPopup(orgPanel, target);
}
// @Override
// protected void setDialogSize() {
// getPageBase().getMainPopup().setInitialWidth(900);
// getPageBase().getMainPopup().setInitialHeight(700);
// }
};
orgUnitEditor.setOutputMarkupId(true);
return orgUnitEditor;
}
private AssignmentEditorDto createTenantOrgDto(){
AssignmentEditorDtoType aType = AssignmentEditorDtoType.ROLE; //doesn't matter the type
ObjectReferenceType targetRef = new ObjectReferenceType();
targetRef.setOid("");
targetRef.setType(aType.getQname());
targetRef.setTargetName(new PolyStringType(""));
AssignmentType assignment = new AssignmentType();
assignment.setTargetRef(targetRef);
AssignmentEditorDto tenantOrgDto = new AssignmentEditorDto(UserDtoStatus.ADD, assignment, getPageBase());
if (tenantEditorObject != null && tenantEditorObject.size() > 0) {
OrgType org = tenantEditorObject.get(0);
ObjectViewDto<OrgType> dto = new ObjectViewDto(org.getOid(),
tenantEditorObject.get(0).getName() == null ? "" : tenantEditorObject.get(0).getName().toString());
dto.setType(OrgType.class);
tenantOrgDto.setTenantRef(dto);
}
if (orgEditorObject != null && orgEditorObject.size() > 0) {
OrgType org = orgEditorObject.get(0);
ObjectViewDto<OrgType> dto = new ObjectViewDto(org.getOid(),
orgEditorObject.get(0).getName() == null ? "" : orgEditorObject.get(0).getName().toString());
dto.setType(OrgType.class);
tenantOrgDto.setOrgRef(dto);
}
return tenantOrgDto;
}
private void setTenantAndOrgToAssignmentsList(List<AssignmentEditorDto> selectedItems){
AssignmentEditorDto tenantOrgDto = createTenantOrgDto();
for (AssignmentEditorDto dto : selectedItems){
if (dto.isSelected()) {
dto.setTenantRef(tenantOrgDto.getTenantRef());
dto.setOrgRef(tenantOrgDto.getOrgRef());
}
}
}
private List<AssignmentEditorDto> getAssignmentsByType(List<AssignmentEditorDto> assignmentsList) {
List<AssignmentEditorDto> currentUsersAssignments = new ArrayList<>();
for (AssignmentEditorDto dto : assignmentsList) {
if (dto.getType().equals(AssignmentEditorDtoType.getType(type)) && !dto.getStatus().equals(UserDtoStatus.DELETE)) {
currentUsersAssignments.add(dto);
}
}
return currentUsersAssignments;
}
private IModel<List<OrgType>> createTenantModel(){
return new IModel<List<OrgType>>() {
@Override
public List<OrgType> getObject() {
return tenantEditorObject;
}
@Override
public void setObject(List<OrgType> orgTypes) {
tenantEditorObject.clear();
tenantEditorObject.addAll(orgTypes);
}
@Override
public void detach() {
}
};
}
private IModel<List<OrgType>> createOrgUnitModel(){
return new IModel<List<OrgType>>() {
@Override
public List<OrgType> getObject() {
return orgEditorObject;
}
@Override
public void setObject(List<OrgType> orgTypes) {
orgEditorObject.clear();
orgEditorObject.addAll(orgTypes);
}
@Override
public void detach() {
}
};
}
private WebMarkupContainer getTenantEditorContainer (){
return (WebMarkupContainer) get(ID_FORM).get(ID_TENANT_EDITOR);
}
private WebMarkupContainer getOrgUnitEditorContainer (){
return (WebMarkupContainer) get(ID_FORM).get(ID_ORG_EDITOR);
}
private boolean areEqualReferenceObjects(ObjectViewDto<OrgType> objRef1, ObjectViewDto<OrgType> objRef2){
return (objRef1 == null && objRef2 == null) ||
(objRef1 != null && objRef2 != null && objRef1.getOid() == null && objRef2.getOid() == null) ||
(objRef1 != null && objRef2 != null && objRef1.getOid() != null &&
objRef2.getOid() != null && objRef1.getOid().equals(objRef2.getOid()));
}
private void reset(MultipleAssignmentSelector panel) {
List<AssignmentEditorDto> assignmentsList = (List<AssignmentEditorDto>)panel.getModel().getObject();
List<AssignmentEditorDto> listToBeRemoved = new ArrayList<>();
for (AssignmentEditorDto dto : assignmentsList){
if (dto.getStatus().equals(UserDtoStatus.ADD)) {
listToBeRemoved.add(dto);
} else if (dto.getStatus() == UserDtoStatus.DELETE) {
dto.setStatus(UserDtoStatus.MODIFY);
}
}
assignmentsList.removeAll(listToBeRemoved);
}
private String getAssignmentDtoOid(AssignmentEditorDto assignmentDto){
ObjectReferenceType assignmentDtoTargetRef = assignmentDto.getTargetRef();
String assignmentDtoOid = null;
if (assignmentDtoTargetRef != null){
assignmentDtoOid = assignmentDtoTargetRef.getOid();
} else {
AssignmentType oldValue = assignmentDto.getOldValue() != null ? assignmentDto.getOldValue().getValue() : null;
assignmentDtoOid = oldValue != null ?
(oldValue.getTarget() == null ? null : oldValue.getTarget().getOid())
: null;
}
return assignmentDtoOid;
}
}