/*
* Copyright (c) 2010-2013 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.dto;
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.model.api.ModelInteractionService;
import com.evolveum.midpoint.model.api.visualizer.Scene;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.schema.DeltaConvertor;
import com.evolveum.midpoint.schema.ObjectTreeDeltas;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.WfContextUtil;
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.web.component.DateLabelComponent;
import com.evolveum.midpoint.web.component.prism.show.SceneDto;
import com.evolveum.midpoint.web.component.prism.show.SceneUtil;
import com.evolveum.midpoint.web.component.util.Selectable;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskChangesDto;
import com.evolveum.midpoint.web.page.admin.server.dto.TaskDto;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import static org.apache.commons.collections.CollectionUtils.addIgnoreNull;
/**
* @author lazyman
* @author mederly
*/
public class WorkItemDto extends Selectable {
public static final String F_WORK_ITEM = "workItem";
public static final String F_NAME = "name";
public static final String F_CREATED_FORMATTED = "createdFormatted";
public static final String F_CREATED_FORMATTED_FULL = "createdFormattedFull";
public static final String F_DEADLINE_FORMATTED = "deadlineFormatted";
public static final String F_DEADLINE_FORMATTED_FULL = "deadlineFormattedFull";
public static final String F_STARTED_FORMATTED_FULL = "startedFormattedFull";
public static final String F_ASSIGNEE_OR_CANDIDATES = "assigneeOrCandidates";
public static final String F_ORIGINAL_ASSIGNEE = "originalAssignee";
public static final String F_ORIGINAL_ASSIGNEE_FULL = "originalAssigneeFull";
public static final String F_CURRENT_ASSIGNEES = "currentAssignees";
public static final String F_CURRENT_ASSIGNEES_FULL = "currentAssigneesFull";
public static final String F_CANDIDATES = "candidates";
public static final String F_STAGE_INFO = "stageInfo";
public static final String F_ESCALATION_LEVEL_INFO = "escalationLevelInfo";
public static final String F_ESCALATION_LEVEL_NUMBER = "escalationLevelNumber";
public static final String F_ADDITIONAL_INFORMATION = "additionalInformation";
public static final String F_OTHER_WORK_ITEMS = "otherWorkItems";
public static final String F_RELATED_WORKFLOW_REQUESTS = "relatedWorkflowRequests";
public static final String F_OBJECT_NAME = "objectName";
public static final String F_TARGET_NAME = "targetName";
public static final String F_REQUESTER_NAME = "requesterName";
public static final String F_REQUESTER_FULL_NAME = "requesterFullName";
public static final String F_APPROVER_COMMENT = "approverComment";
public static final String F_WORKFLOW_CONTEXT = "workflowContext"; // use with care
@Deprecated public static final String F_DELTAS = "deltas";
public static final String F_PROCESS_INSTANCE_ID = "processInstanceId";
public static final String F_CHANGES = "changes";
public static final String F_REQUESTER_COMMENT = "requesterComment";
// workItem may or may not contain resolved taskRef;
// and this task may or may not contain filled-in workflowContext -> and then requesterRef object
//
// Depending on expected use (work item list vs. work item details)
private WorkItemType workItem;
private TaskType taskType;
private List<TaskType> relatedTasks;
@Deprecated private SceneDto deltas;
private TaskChangesDto changes;
private String approverComment;
private ObjectType focus;
public WorkItemDto(WorkItemType workItem) {
this(workItem, null, null);
}
public WorkItemDto(WorkItemType workItem, TaskType taskType, List<TaskType> relatedTasks) {
this.workItem = workItem;
this.taskType = taskType;
this.relatedTasks = relatedTasks;
}
public void prepareDeltaVisualization(String sceneNameKey, PrismContext prismContext,
ModelInteractionService modelInteractionService, Task opTask, OperationResult result) throws SchemaException {
TaskType task = getTaskType();
if (task == null || task.getWorkflowContext() == null) {
return;
}
if (!(task.getWorkflowContext().getProcessorSpecificState() instanceof WfPrimaryChangeProcessorStateType)) {
return;
}
WfPrimaryChangeProcessorStateType state = (WfPrimaryChangeProcessorStateType) task.getWorkflowContext().getProcessorSpecificState();
Scene deltasScene = SceneUtil.visualizeObjectTreeDeltas(state.getDeltasToProcess(), sceneNameKey, prismContext, modelInteractionService, opTask, result);
deltas = new SceneDto(deltasScene);
ObjectTreeDeltas deltas = ObjectTreeDeltas.fromObjectTreeDeltasType(state.getDeltasToProcess(), prismContext);
changes = TaskDto.createChangesToBeApproved(deltas, modelInteractionService,
prismContext, opTask, result);
}
@Nullable
private TaskType getTaskType() {
if (taskType == null) {
//taskType = WebComponentUtil.getObjectFromReference(workItem.getTaskRef(), TaskType.class);
taskType = WfContextUtil.getTask(workItem);
}
return taskType;
}
public String getWorkItemId() {
return workItem.getExternalId();
}
public String getName() {
return workItem.getName();
}
public String getCreatedFormatted() {
return WebComponentUtil.getLocalizedDate(workItem.getCreateTimestamp(), DateLabelComponent.MEDIUM_MEDIUM_STYLE);
}
public String getDeadlineFormatted() {
return WebComponentUtil.getLocalizedDate(workItem.getDeadline(), DateLabelComponent.MEDIUM_MEDIUM_STYLE);
}
public String getCreatedFormattedFull() {
return WebComponentUtil.getLocalizedDate(workItem.getCreateTimestamp(), DateLabelComponent.FULL_MEDIUM_STYLE);
}
public String getDeadlineFormattedFull() {
return WebComponentUtil.getLocalizedDate(workItem.getDeadline(), DateLabelComponent.FULL_MEDIUM_STYLE);
}
public Date getCreatedDate() {
return XmlTypeConverter.toDate(workItem.getCreateTimestamp());
}
public Date getDeadlineDate() {
return XmlTypeConverter.toDate(workItem.getDeadline());
}
public Date getStartedDate() {
return XmlTypeConverter.toDate(getWorkflowContext().getStartTimestamp()); // TODO NPE?
}
public String getStartedFormattedFull() {
return WebComponentUtil.getLocalizedDate(getWorkflowContext().getStartTimestamp(), DateLabelComponent.FULL_MEDIUM_STYLE);
}
// TODO
public String getAssigneeOrCandidates() {
String assignee = getCurrentAssignees();
if (assignee != null) {
return assignee;
} else {
return getCandidates();
}
}
public String getOriginalAssignee() {
return WebComponentUtil.getReferencedObjectNames(Collections.singletonList(workItem.getOriginalAssigneeRef()), false);
}
public String getOriginalAssigneeFull() {
return WebComponentUtil.getReferencedObjectDisplayNamesAndNames(Collections.singletonList(workItem.getOriginalAssigneeRef()), false);
}
public String getCurrentAssignees() {
return WebComponentUtil.getReferencedObjectNames(workItem.getAssigneeRef(), false);
}
public String getCurrentAssigneesFull() {
return WebComponentUtil.getReferencedObjectDisplayNamesAndNames(workItem.getAssigneeRef(), false);
}
public String getCandidates() {
return WebComponentUtil.getReferencedObjectNames(workItem.getCandidateRef(), true);
}
public String getObjectName() {
return WebComponentUtil.getName(WfContextUtil.getObjectRef(workItem));
}
public ObjectReferenceType getObjectRef() {
return WfContextUtil.getObjectRef(workItem);
}
public ObjectReferenceType getTargetRef() {
return WfContextUtil.getTargetRef(workItem);
}
public String getTargetName() {
return WebComponentUtil.getName(WfContextUtil.getTargetRef(workItem));
}
public WfContextType getWorkflowContext() {
TaskType task = getTaskType();
return task != null ? task.getWorkflowContext() : null;
}
public String getRequesterName() {
WfContextType workflowContext = getWorkflowContext();
return workflowContext != null ? WebComponentUtil.getName(workflowContext.getRequesterRef()) : null;
}
public String getRequesterFullName() {
UserType requester = getRequester();
return requester != null ? PolyString.getOrig(requester.getFullName()) : null;
}
public UserType getRequester() {
WfContextType wfContext = getWorkflowContext();
if (wfContext == null) {
return null;
}
return WebComponentUtil.getObjectFromReference(wfContext.getRequesterRef(), UserType.class);
}
public String getApproverComment() {
return approverComment;
}
public void setApproverComment(String approverComment) {
this.approverComment = approverComment;
}
public WorkItemType getWorkItem() {
return workItem;
}
@Deprecated
public SceneDto getDeltas() {
return deltas;
}
public QName getTargetType() {
ObjectReferenceType targetRef = WfContextUtil.getTargetRef(workItem);
return targetRef != null ? targetRef.getType() : null;
}
public QName getObjectType() {
ObjectReferenceType objectRef = WfContextUtil.getObjectRef(workItem);
return objectRef != null ? objectRef.getType() : null;
}
// all except the current one
public List<WorkItemDto> getOtherWorkItems() {
final List<WorkItemDto> rv = new ArrayList<>();
final TaskType task = getTaskType();
if (task == null || task.getWorkflowContext() == null) {
return rv;
}
for (WorkItemType workItemType : task.getWorkflowContext().getWorkItem()) {
if (workItemType.getExternalId() == null || workItemType.getExternalId().equals(getWorkItemId())) {
continue;
}
rv.add(new WorkItemDto(workItemType));
}
return rv;
}
// all excluding the current task
public List<ProcessInstanceDto> getRelatedWorkflowRequests() {
final List<ProcessInstanceDto> rv = new ArrayList<>();
if (relatedTasks == null) {
return rv;
}
for (TaskType task : relatedTasks) {
if (task.getWorkflowContext() == null || task.getWorkflowContext().getProcessInstanceId() == null) {
continue;
}
if (StringUtils.equals(getProcessInstanceId(), task.getWorkflowContext().getProcessInstanceId())) {
continue;
}
rv.add(new ProcessInstanceDto(task));
}
return rv;
}
public String getProcessInstanceId() {
final TaskType task = getTaskType();
return task != null && task.getWorkflowContext() != null ? task.getWorkflowContext().getProcessInstanceId() : null;
}
public String getTaskOid() {
final TaskType task = getTaskType();
return task != null ? task.getOid() : null;
}
// TODO deduplicate
public boolean hasHistory() {
List<DecisionDto> rv = new ArrayList<>();
WfContextType wfContextType = getWorkflowContext();
if (wfContextType == null) {
return false;
}
if (!wfContextType.getEvent().isEmpty()) {
wfContextType.getEvent().forEach(e -> addIgnoreNull(rv, DecisionDto.create(e, null)));
} else {
ItemApprovalProcessStateType instanceState = WfContextUtil.getItemApprovalProcessInfo(wfContextType);
if (instanceState != null) {
instanceState.getDecisions().forEach(d -> addIgnoreNull(rv, DecisionDto.create(d)));
}
}
return !rv.isEmpty();
}
public String getStageInfo() {
WfContextType wfc = getWorkflowContext(); // wfc contains also the approval schema
return wfc != null ? WfContextUtil.getStageInfo(wfc) : WfContextUtil.getStageInfo(workItem);
}
public String getEscalationLevelInfo() {
return WfContextUtil.getEscalationLevelInfo(workItem);
}
public Integer getEscalationLevelNumber() {
int number = WfContextUtil.getEscalationLevelNumber(workItem);
return number > 0 ? number : null;
}
public List<InformationType> getAdditionalInformation() {
return workItem.getAdditionalInformation();
}
// Expects that we deal with primary changes of the focus (i.e. not of projections)
// Beware: returns the full object; regardless of the security settings
public ObjectType getFocus(PageBase pageBase) {
if (focus != null) {
return focus;
}
WfContextType wfc = getWorkflowContext();
WfPrimaryChangeProcessorStateType state = WfContextUtil.getPrimaryChangeProcessorState(wfc);
if (state == null || state.getDeltasToProcess() == null || state.getDeltasToProcess().getFocusPrimaryDelta() == null) {
return null;
}
ObjectDeltaType delta = state.getDeltasToProcess().getFocusPrimaryDelta();
if (delta.getChangeType() == ChangeTypeType.ADD) {
focus = CloneUtil.clone((ObjectType) delta.getObjectToAdd());
} else if (delta.getChangeType() == ChangeTypeType.MODIFY) {
String oid = delta.getOid();
if (oid == null) {
throw new IllegalStateException("No OID in object modify delta: " + delta);
}
if (delta.getObjectType() == null) {
throw new IllegalStateException("No object type in object modify delta: " + delta);
}
Class<? extends ObjectType> clazz = ObjectTypes.getObjectTypeFromTypeQName(delta.getObjectType())
.getClassDefinition();
Task task = pageBase.createSimpleTask("getObject");
PrismObject<?> object = pageBase.getSecurityEnforcer().runPrivileged(() ->
WebModelServiceUtils.loadObject(clazz, oid, pageBase, task, task.getResult()));
if (object != null) {
focus = (ObjectType) object.asObjectable();
try {
ObjectDelta<Objectable> objectDelta = DeltaConvertor.createObjectDelta(delta, pageBase.getPrismContext());
objectDelta.applyTo((PrismObject) focus.asPrismObject());
} catch (SchemaException e) {
throw new SystemException("Cannot apply delta to focus object: " + e.getMessage(), e);
}
focus = (ObjectType) object.asObjectable();
}
} else {
// DELETE case: nothing to do here
}
return focus;
}
public String getRequesterComment() {
OperationBusinessContextType businessContext = WfContextUtil.getBusinessContext(getWorkflowContext());
return businessContext != null ? businessContext.getComment() : null;
}
}