/*
* 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.page.admin.workflow;
import com.evolveum.midpoint.gui.api.component.ObjectBrowserPanel;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.model.api.WorkflowService;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.WfContextUtil;
import com.evolveum.midpoint.security.api.AuthorizationConstants;
import com.evolveum.midpoint.security.api.MidPointPrincipal;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.*;
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.application.AuthorizationAction;
import com.evolveum.midpoint.web.application.PageDescriptor;
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.component.AjaxSubmitButton;
import com.evolveum.midpoint.web.component.DefaultAjaxButton;
import com.evolveum.midpoint.web.component.DefaultAjaxSubmitButton;
import com.evolveum.midpoint.web.component.util.VisibleBehaviour;
import com.evolveum.midpoint.web.page.admin.home.PageDashboard;
import com.evolveum.midpoint.web.page.admin.workflow.dto.WorkItemDto;
import com.evolveum.midpoint.web.util.OnePageParameterEncoder;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static com.evolveum.midpoint.schema.GetOperationOptions.resolveItemsNamed;
import static com.evolveum.midpoint.schema.GetOperationOptions.retrieveItemsNamed;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_PARENT;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_REQUESTER_REF;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_WORK_ITEM;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType.F_ASSIGNEE_REF;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType.F_ORIGINAL_ASSIGNEE_REF;
import static com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType.F_EXTERNAL_ID;
/**
* @author mederly
*/
@PageDescriptor(url = "/admin/workItem", encoder = OnePageParameterEncoder.class, action = {
@AuthorizationAction(actionUri = PageAdminWorkItems.AUTH_APPROVALS_ALL,
label = PageAdminWorkItems.AUTH_APPROVALS_ALL_LABEL,
description = PageAdminWorkItems.AUTH_APPROVALS_ALL_DESCRIPTION),
@AuthorizationAction(actionUri = AuthorizationConstants.AUTZ_UI_WORK_ITEM_URL,
label = "PageWorkItem.auth.workItem.label",
description = "PageWorkItem.auth.workItem.description")})
public class PageWorkItem extends PageAdminWorkItems {
private static final String DOT_CLASS = PageWorkItem.class.getName() + ".";
private static final String OPERATION_LOAD_WORK_ITEM = DOT_CLASS + "loadWorkItem";
private static final String OPERATION_SAVE_WORK_ITEM = DOT_CLASS + "saveWorkItem";
private static final String OPERATION_DELEGATE_WORK_ITEM = DOT_CLASS + "delegateWorkItem";
private static final String OPERATION_CLAIM_WORK_ITEM = DOT_CLASS + "claimWorkItem";
private static final String OPERATION_RELEASE_WORK_ITEM = DOT_CLASS + "releaseWorkItem";
private static final String ID_SUMMARY_PANEL = "summaryPanel";
private static final String ID_WORK_ITEM_PANEL = "workItemPanel";
private static final Trace LOGGER = TraceManager.getTrace(PageWorkItem.class);
private static final String ID_MAIN_FORM = "mainForm";
private static final String ID_CLAIM = "claim";
private static final String ID_RELEASE = "release";
private static final String ID_APPROVE = "approve";
private static final String ID_REJECT = "reject";
private static final String ID_DELEGATE = "delegate";
private static final String ID_CANCEL = "cancel";
private LoadableModel<WorkItemDto> workItemDtoModel;
private String taskId;
public PageWorkItem(PageParameters parameters) {
taskId = parameters.get(OnePageParameterEncoder.PARAMETER).toString();
if (taskId == null) {
throw new IllegalStateException("Work item ID not specified.");
}
workItemDtoModel = new LoadableModel<WorkItemDto>(false) {
@Override
protected WorkItemDto load() {
return loadWorkItemDtoIfNecessary();
}
};
initLayout();
}
@Override
protected void createBreadcrumb() {
createInstanceBreadcrumb(); // to preserve page state (e.g. approver's comment)
}
private WorkItemDto loadWorkItemDtoIfNecessary() {
if (workItemDtoModel.isLoaded()) {
return workItemDtoModel.getObject();
}
Task task = createSimpleTask(OPERATION_LOAD_WORK_ITEM);
OperationResult result = task.getResult();
WorkItemDto workItemDto = null;
try {
final ObjectQuery query = QueryBuilder.queryFor(WorkItemType.class, getPrismContext())
.item(F_EXTERNAL_ID).eq(taskId)
.build();
final Collection<SelectorOptions<GetOperationOptions>> options =
resolveItemsNamed(F_ASSIGNEE_REF, F_ORIGINAL_ASSIGNEE_REF);
List<WorkItemType> workItems = getModelService().searchContainers(WorkItemType.class, query, options, task, result);
if (workItems.size() > 1) {
throw new SystemException("More than one work item with ID of " + taskId);
} else if (workItems.size() == 0) {
throw new SystemException("No work item with ID of " + taskId);
}
final WorkItemType workItem = workItems.get(0);
//final String taskOid = workItem.getTaskRef() != null ? workItem.getTaskRef().getOid() : null;
final String taskOid = WfContextUtil.getTask(workItem).getOid();
TaskType taskType = null;
List<TaskType> relatedTasks = new ArrayList<>();
if (taskOid != null) {
final Collection<SelectorOptions<GetOperationOptions>> getTaskOptions = resolveItemsNamed(
new ItemPath(F_WORKFLOW_CONTEXT, F_REQUESTER_REF));
getTaskOptions.addAll(retrieveItemsNamed(new ItemPath(F_WORKFLOW_CONTEXT, F_WORK_ITEM)));
try {
taskType = getModelService().getObject(TaskType.class, taskOid, getTaskOptions, task, result).asObjectable();
} catch (AuthorizationException e) {
LoggingUtils.logExceptionOnDebugLevel(LOGGER, "Access to the task {} was denied", e, taskOid);
}
if (taskType != null && taskType.getParent() != null) {
final ObjectQuery relatedTasksQuery = QueryBuilder.queryFor(TaskType.class, getPrismContext())
.item(F_PARENT).eq(taskType.getParent())
.build();
List<PrismObject<TaskType>> relatedTaskObjects = getModelService()
.searchObjects(TaskType.class, relatedTasksQuery, null, task, result);
for (PrismObject<TaskType> relatedObject : relatedTaskObjects) {
relatedTasks.add(relatedObject.asObjectable());
}
}
}
workItemDto = new WorkItemDto(workItem, taskType, relatedTasks);
workItemDto.prepareDeltaVisualization("pageWorkItem.delta", getPrismContext(), getModelInteractionService(), task, result);
result.recordSuccessIfUnknown();
} catch (CommonException|RuntimeException ex) {
result.recordFatalError("Couldn't get work item.", ex);
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't get work item.", ex);
}
showResult(result, false);
if (!result.isSuccess()) {
throw getRestartResponseException(PageDashboard.class);
}
return workItemDto;
}
private void initLayout() {
WorkItemSummaryPanel summaryPanel = new WorkItemSummaryPanel(ID_SUMMARY_PANEL,
new PropertyModel<>(workItemDtoModel, WorkItemDto.F_WORK_ITEM), workItemDtoModel);
add(summaryPanel);
Form mainForm = new Form(ID_MAIN_FORM);
mainForm.setMultiPart(true);
add(mainForm);
mainForm.add(new WorkItemPanel(ID_WORK_ITEM_PANEL, workItemDtoModel, mainForm, this));
initButtons(mainForm);
}
public WorkItemPanel getWorkItemPanel() {
return (WorkItemPanel) get(ID_MAIN_FORM).get(ID_WORK_ITEM_PANEL);
}
private void initButtons(Form mainForm) {
VisibleBehaviour isAllowedToSubmit = new VisibleBehaviour(() ->
getWorkflowManager().isCurrentUserAuthorizedToSubmit(workItemDtoModel.getObject().getWorkItem()));
VisibleBehaviour isAllowedToDelegate = new VisibleBehaviour(() ->
getWorkflowManager().isCurrentUserAuthorizedToDelegate(workItemDtoModel.getObject().getWorkItem()));
VisibleBehaviour isAllowedToClaim = new VisibleBehaviour(() ->
workItemDtoModel.getObject().getWorkItem().getAssigneeRef() == null &&
getWorkflowManager().isCurrentUserAuthorizedToClaim(workItemDtoModel.getObject().getWorkItem()));
VisibleBehaviour isAllowedToRelease = new VisibleBehaviour(() -> {
WorkItemType workItem = workItemDtoModel.getObject().getWorkItem();
MidPointPrincipal principal;
try {
principal = (MidPointPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
} catch (ClassCastException e) {
return false;
}
String principalOid = principal.getOid();
return workItem.getAssigneeRef() != null
&& workItem.getAssigneeRef().stream().anyMatch(ref -> ref.getOid().equals(principalOid))
&& (!workItem.getCandidateRef().isEmpty());
});
AjaxSubmitButton claim = new DefaultAjaxSubmitButton(ID_CLAIM, createStringResource("pageWorkItem.button.claim"),
this, (target, form) -> claimPerformed(target));
claim.add(isAllowedToClaim);
mainForm.add(claim);
AjaxSubmitButton release = new DefaultAjaxSubmitButton(ID_RELEASE, createStringResource("pageWorkItem.button.release"),
this, (target, form) -> releasePerformed(target));
release.add(isAllowedToRelease);
mainForm.add(release);
AjaxSubmitButton approve = new DefaultAjaxSubmitButton(ID_APPROVE, createStringResource("pageWorkItem.button.approve"),
this, (target, form) -> savePerformed(target, true));
approve.add(isAllowedToSubmit);
mainForm.add(approve);
AjaxSubmitButton reject = new DefaultAjaxSubmitButton(ID_REJECT, createStringResource("pageWorkItem.button.reject"),
this, (target, form) -> savePerformed(target, false));
reject.add(isAllowedToSubmit);
mainForm.add(reject);
AjaxSubmitButton delegate = new DefaultAjaxSubmitButton(ID_DELEGATE, createStringResource("pageWorkItem.button.delegate"),
this, (target, form) -> delegatePerformed(target));
delegate.add(isAllowedToDelegate);
mainForm.add(delegate);
AjaxButton cancel = new DefaultAjaxButton(ID_CANCEL, createStringResource("pageWorkItem.button.cancel"), this::cancelPerformed);
mainForm.add(cancel);
}
@SuppressWarnings("unused")
private void cancelPerformed(AjaxRequestTarget target) {
redirectBack();
}
private void savePerformed(AjaxRequestTarget target, boolean decision) {
OperationResult result = new OperationResult(OPERATION_SAVE_WORK_ITEM);
try {
WorkItemDto dto = workItemDtoModel.getObject();
ObjectDelta delta = getWorkItemPanel().getDeltaFromForm();
getWorkflowService().completeWorkItem(dto.getWorkItemId(), decision, dto.getApproverComment(), delta, result);
} catch (Exception ex) {
result.recordFatalError("Couldn't save work item.", ex);
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't save work item", ex);
}
processResult(target, result, false);
}
private void delegatePerformed(AjaxRequestTarget target) {
ObjectBrowserPanel<UserType> panel = new ObjectBrowserPanel<UserType>(
getMainPopupBodyId(), UserType.class,
Collections.singletonList(UserType.COMPLEX_TYPE), false, this, null) {
private static final long serialVersionUID = 1L;
@Override
protected void onSelectPerformed(AjaxRequestTarget target, UserType user) {
hideMainPopup(target);
delegateConfirmedPerformed(target, user);
}
};
panel.setOutputMarkupId(true);
showMainPopup(panel, target);
}
private void delegateConfirmedPerformed(AjaxRequestTarget target, UserType delegate) {
OperationResult result = new OperationResult(OPERATION_DELEGATE_WORK_ITEM);
try {
WorkItemDto dto = workItemDtoModel.getObject();
List<ObjectReferenceType> delegates = Collections.singletonList(ObjectTypeUtil.createObjectRef(delegate));
getWorkflowService().delegateWorkItem(dto.getWorkItemId(), delegates, WorkItemDelegationMethodType.ADD_ASSIGNEES, result);
} catch (Exception ex) {
result.recordFatalError("Couldn't delegate work item.", ex);
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't delegate work item", ex);
}
processResult(target, result, false);
}
private void claimPerformed(AjaxRequestTarget target) {
OperationResult result = new OperationResult(OPERATION_CLAIM_WORK_ITEM);
WorkflowService workflowService = getWorkflowService();
try {
workflowService.claimWorkItem(workItemDtoModel.getObject().getWorkItemId(), result);
} catch (SecurityViolationException | ObjectNotFoundException | RuntimeException e) {
result.recordFatalError("Couldn't claim work item due to an unexpected exception.", e);
}
processResult(target, result, true);
}
private void releasePerformed(AjaxRequestTarget target) {
OperationResult result = new OperationResult(OPERATION_RELEASE_WORK_ITEM);
WorkflowService workflowService = getWorkflowService();
try {
workflowService.releaseWorkItem(workItemDtoModel.getObject().getWorkItem().getExternalId(), result);
} catch (SecurityViolationException | ObjectNotFoundException | RuntimeException e) {
result.recordFatalError("Couldn't release work item due to an unexpected exception.", e);
}
processResult(target, result, true);
}
}