| /********************************************************************* |
| * Copyright (c) 2011 Boeing |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Boeing - initial API and implementation |
| **********************************************************************/ |
| |
| package org.eclipse.osee.ats.ide.editor.tab.workflow.header; |
| |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.logging.Level; |
| import org.eclipse.core.runtime.jobs.IJobChangeEvent; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.jobs.JobChangeAdapter; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.osee.ats.api.AtsApi; |
| import org.eclipse.osee.ats.api.IAtsWorkItem; |
| import org.eclipse.osee.ats.api.data.AtsArtifactTypes; |
| import org.eclipse.osee.ats.api.data.AtsAttributeTypes; |
| import org.eclipse.osee.ats.api.user.AtsUser; |
| import org.eclipse.osee.ats.api.util.IAtsChangeSet; |
| import org.eclipse.osee.ats.api.workdef.IAtsStateDefinition; |
| import org.eclipse.osee.ats.api.workdef.StateOption; |
| import org.eclipse.osee.ats.api.workflow.hooks.IAtsTransitionHook; |
| import org.eclipse.osee.ats.api.workflow.hooks.IAtsWorkItemHook; |
| import org.eclipse.osee.ats.api.workflow.transition.ITransitionHelper; |
| import org.eclipse.osee.ats.api.workflow.transition.TransitionData; |
| import org.eclipse.osee.ats.api.workflow.transition.TransitionResults; |
| import org.eclipse.osee.ats.core.workflow.transition.TransitionHelperAdapter; |
| import org.eclipse.osee.ats.ide.editor.WorkflowEditor; |
| import org.eclipse.osee.ats.ide.internal.Activator; |
| import org.eclipse.osee.ats.ide.internal.AtsApiService; |
| import org.eclipse.osee.ats.ide.util.AtsUtilClient; |
| import org.eclipse.osee.ats.ide.util.UserCheckTreeDialog; |
| import org.eclipse.osee.ats.ide.util.widgets.dialog.CancelledReasonEnumDialog; |
| import org.eclipse.osee.ats.ide.workflow.AbstractWorkflowArtifact; |
| import org.eclipse.osee.ats.ide.workflow.transition.TransitionResultsUi; |
| import org.eclipse.osee.ats.ide.workflow.transition.TransitionToOperation; |
| import org.eclipse.osee.framework.core.operation.Operations; |
| import org.eclipse.osee.framework.jdk.core.type.OseeCoreException; |
| import org.eclipse.osee.framework.jdk.core.util.Collections; |
| import org.eclipse.osee.framework.jdk.core.util.Strings; |
| import org.eclipse.osee.framework.logging.OseeLevel; |
| import org.eclipse.osee.framework.logging.OseeLog; |
| import org.eclipse.osee.framework.ui.plugin.util.AWorkbench; |
| import org.eclipse.osee.framework.ui.plugin.util.ListSelectionDialogNoSave; |
| import org.eclipse.osee.framework.ui.skynet.widgets.XComboDam; |
| import org.eclipse.osee.framework.ui.skynet.widgets.dialog.EntryDialog; |
| import org.eclipse.osee.framework.ui.swt.Displays; |
| import org.eclipse.osee.framework.ui.swt.FontManager; |
| import org.eclipse.osee.framework.ui.swt.Widgets; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.ui.forms.events.HyperlinkAdapter; |
| import org.eclipse.ui.forms.events.HyperlinkEvent; |
| import org.eclipse.ui.forms.widgets.Hyperlink; |
| |
| /** |
| * @author Donald G. Dunne |
| */ |
| public class WfeTransitionHeader extends Composite { |
| |
| private final Label transitionAssigneesLabel; |
| private final AbstractWorkflowArtifact workItem; |
| private final WorkflowEditor editor; |
| private IAtsStateDefinition userSelectedTransitionToState; |
| private final boolean isEditable; |
| private final Hyperlink transitionLabelLink; |
| private final Hyperlink stateLabelLink; |
| |
| public WfeTransitionHeader(Composite parent, final WorkflowEditor editor, final boolean isEditable) { |
| super(parent, SWT.NONE); |
| this.editor = editor; |
| this.isEditable = isEditable; |
| |
| workItem = editor.getWorkItem(); |
| setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| GridLayout layout = new GridLayout(editor.getWorkFlowTab().getHeader().isShowTargetedVersion() ? 7 : 5, false); |
| layout.verticalSpacing = 0; |
| layout.marginWidth = 0; |
| layout.marginHeight = 0; |
| setLayout(layout); |
| editor.getWorkFlowTab().getManagedForm().getToolkit().adapt(this); |
| |
| transitionLabelLink = editor.getToolkit().createHyperlink(this, "Transition", SWT.NONE); |
| transitionLabelLink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| if (editor.isDirty()) { |
| editor.doSave(null); |
| } |
| transitionLabelLink.setEnabled(false); |
| handleTransitionButtonSelection(); |
| } |
| }); |
| transitionLabelLink.setFont(FontManager.getCourierNew12Bold()); |
| transitionLabelLink.setToolTipText("Select to transition workflow to the default or selected state"); |
| |
| Label transitionToLabel = editor.getToolkit().createLabel(this, "To"); |
| transitionToLabel.setLayoutData(new GridData()); |
| |
| String toStateName = getToState() == null ? "<not set>" : getToState().getName(); |
| stateLabelLink = editor.getToolkit().createHyperlink(this, toStateName, SWT.NONE); |
| stateLabelLink.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| stateLabelLink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| try { |
| IAtsStateDefinition selState = handleChangeTransitionToState(workItem, isEditable, getToState()); |
| if (selState != null) { |
| userSelectedTransitionToState = selState; |
| handleTransitionButtonSelection(); |
| } |
| } catch (Exception ex) { |
| OseeLog.log(Activator.class, Level.SEVERE, ex); |
| } |
| } |
| }); |
| |
| Hyperlink assigneesLabelLink = editor.getToolkit().createHyperlink(this, "Next State Assignee(s)", SWT.NONE); |
| assigneesLabelLink.addHyperlinkListener(new HyperlinkAdapter() { |
| @Override |
| public void linkActivated(HyperlinkEvent e) { |
| try { |
| handleChangeTransitionAssignees(workItem); |
| } catch (Exception ex) { |
| OseeLog.log(Activator.class, OseeLevel.SEVERE_POPUP, ex); |
| } |
| } |
| }); |
| transitionAssigneesLabel = |
| editor.getToolkit().createLabel(this, Strings.truncate(workItem.getTransitionAssigneesStr(), 100, true)); |
| transitionAssigneesLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); |
| transitionAssigneesLabel.setToolTipText("Select to change assignee(s) upon transition to next state."); |
| |
| } |
| |
| public static IAtsStateDefinition handleChangeTransitionToState(AbstractWorkflowArtifact awa, final boolean isEditable, IAtsStateDefinition toStateDef) { |
| List<IAtsStateDefinition> states = AtsApiService.get().getWorkItemService().getAllToStates(awa); |
| ListSelectionDialogNoSave dialog = |
| new ListSelectionDialogNoSave(Collections.castAll(states), Displays.getActiveShell().getShell(), |
| "Select Transition-To State", null, "Select the state to transition to.\n\n" // |
| + "Transition will happen upon selection and Transition button.\n\n" // |
| + "Double-click will select, close and transition.", |
| 2, new String[] {"Transition", "Cancel"}, 0); |
| |
| if (dialog.open() == Window.OK) { |
| Object obj = dialog.getSelected(); |
| return (IAtsStateDefinition) obj; |
| } |
| |
| return null; |
| } |
| |
| public void handleTransitionButtonSelection() { |
| final IAtsStateDefinition toStateDef = getToState(); |
| if (toStateDef == null) { |
| AWorkbench.popup("Must select state to transition."); |
| refresh(); |
| return; |
| } |
| if (editor.isDirty()) { |
| editor.doSave(null); |
| } |
| editor.getWorkFlowTab().setLoading(true); |
| handleTransitionButtonSelection(workItem, isEditable, toStateDef, editor, this); |
| } |
| |
| public static void handleTransitionButtonSelection(AbstractWorkflowArtifact awa, final boolean isEditable, IAtsStateDefinition toStateDef, final WorkflowEditor editor, final WfeTransitionHeader transitionHeader) { |
| ITransitionHelper helper = new TransitionHelperAdapter(AtsApiService.get()) { |
| |
| @Override |
| public String getToStateName() { |
| return toStateDef.getName(); |
| } |
| |
| @Override |
| public Collection<AtsUser> getToAssignees(IAtsWorkItem workItem) { |
| AbstractWorkflowArtifact awa = |
| (AbstractWorkflowArtifact) AtsApiService.get().getQueryService().getArtifact(workItem); |
| return awa.getTransitionAssignees(); |
| } |
| |
| @Override |
| public String getName() { |
| return "Workflow Editor Transition"; |
| } |
| |
| @Override |
| public TransitionData getCancellationReason(final TransitionData transitionData) { |
| Displays.ensureInDisplayThread(new Runnable() { |
| |
| @Override |
| public void run() { |
| IAtsStateDefinition toStateDef; |
| try { |
| toStateDef = |
| AtsApiService.get().getWorkDefinitionService().getStateDefinitionByName(awa, getToStateName()); |
| if (toStateDef.getStateType().isCancelledState()) { |
| EntryDialog cancelDialog; |
| boolean useCancelledReasonEnumDialog = |
| toStateDef.getStateOptions().contains(StateOption.USE_CANCELLED_REASON_ENUM_DIALOG); |
| if (useCancelledReasonEnumDialog) { |
| cancelDialog = new CancelledReasonEnumDialog("Cancellation Reason", |
| "Select cancellation reason. If other, please specify with details."); |
| } else { |
| cancelDialog = new EntryDialog("Cancellation Reason", "Enter cancellation reason."); |
| } |
| if (cancelDialog.open() != 0) { |
| transitionData.setDialogCancelled(true); |
| return; |
| } |
| if (useCancelledReasonEnumDialog) { |
| transitionData.setCancellationReason(((CancelledReasonEnumDialog) cancelDialog).getEntry()); |
| transitionData.setCancellationReasonAttrType(AtsAttributeTypes.CancelledReasonEnum); |
| transitionData.setCancellationReasonDetails( |
| ((CancelledReasonEnumDialog) cancelDialog).getCancelledDetails()); |
| } else { |
| transitionData.setCancellationReason(cancelDialog.getEntry()); |
| transitionData.setCancellationReasonAttrType(AtsAttributeTypes.CancelledReason); |
| } |
| } |
| } catch (OseeCoreException ex) { |
| OseeLog.log(Activator.class, Level.SEVERE, ex); |
| } |
| } |
| }, true); |
| return transitionData; |
| } |
| |
| @Override |
| public Collection<IAtsWorkItem> getWorkItems() { |
| return Arrays.asList(awa); |
| } |
| |
| @Override |
| public IAtsChangeSet getChangeSet() { |
| return null; |
| } |
| |
| @Override |
| public Collection<IAtsTransitionHook> getTransitionListeners() { |
| try { |
| return AtsApiService.get().getWorkItemService().getTransitionHooks(); |
| } catch (OseeCoreException ex) { |
| OseeLog.log(Activator.class, Level.SEVERE, ex); |
| } |
| return java.util.Collections.emptyList(); |
| } |
| |
| @Override |
| public AtsApi getServices() { |
| return AtsApiService.get(); |
| } |
| |
| @Override |
| public String getCancellationReasonDetails() { |
| return null; |
| } |
| |
| @Override |
| public String getCancellationReason() { |
| return null; |
| } |
| |
| }; |
| final TransitionToOperation operation = new TransitionToOperation(helper); |
| Operations.executeAsJob(operation, true, Job.SHORT, new JobChangeAdapter() { |
| |
| @Override |
| public void done(IJobChangeEvent event) { |
| TransitionResults results = operation.getResults(); |
| results.setAtsApi(AtsApiService.get()); |
| if (results.isErrors()) { |
| TransitionResultsUi.reportDialog("Transition Failed", results); |
| AtsUtilClient.logExceptions(results); |
| } |
| editor.getWorkFlowTab().setLoading(false); |
| transitionHeader.refresh(); |
| } |
| |
| }); |
| } |
| |
| public void updateTransitionToAssignees() { |
| Collection<AtsUser> assignees = null; |
| // Determine if the is an override set of assignees |
| for (IAtsWorkItemHook item : AtsApiService.get().getWorkItemService().getWorkItemHooks()) { |
| String decisionValueIfApplicable = ""; |
| if (workItem.isOfType( |
| AtsArtifactTypes.DecisionReview) && editor.getWorkFlowTab().getCurrentStateSection().getPage().getLayoutData( |
| AtsAttributeTypes.Decision.getName()) != null) { |
| XComboDam xWidget = (XComboDam) editor.getWorkFlowTab().getCurrentStateSection().getPage().getLayoutData( |
| AtsAttributeTypes.Decision.getName()).getXWidget(); |
| if (xWidget != null) { |
| decisionValueIfApplicable = xWidget.get(); |
| } |
| } |
| assignees = item.getOverrideTransitionToAssignees(workItem, decisionValueIfApplicable); |
| if (assignees != null) { |
| break; |
| } |
| } |
| // If override set and isn't the same as already selected, update |
| if (assignees != null && !workItem.getTransitionAssignees().equals(assignees)) { |
| workItem.setTransitionAssignees(assignees); |
| editor.onDirtied(); |
| } |
| refresh(); |
| } |
| |
| public IAtsStateDefinition getToState() { |
| IAtsStateDefinition state = AtsApiService.get().getWorkItemService().getDefaultToState(workItem); |
| if (userSelectedTransitionToState != null) { |
| return userSelectedTransitionToState; |
| } |
| // Determine if there is a transitionToStateOverride for this page |
| String transitionStateOverride = null; |
| for (IAtsTransitionHook item : AtsApiService.get().getWorkItemService().getTransitionHooks()) { |
| transitionStateOverride = item.getOverrideTransitionToStateName(editor.getWorkItem()); |
| if (transitionStateOverride != null) { |
| break; |
| } |
| } |
| if (transitionStateOverride != null) { |
| // Return if override state is same as selected |
| if (AtsApiService.get().getWorkItemService().getDefaultToState(workItem).getName().equals( |
| transitionStateOverride)) { |
| state = AtsApiService.get().getWorkItemService().getDefaultToState(workItem); |
| } |
| // Find page corresponding to override state name |
| for (IAtsStateDefinition toState : workItem.getStateDefinition().getToStates()) { |
| if (toState.getName().equals(transitionStateOverride)) { |
| state = toState; |
| break; |
| } |
| } |
| } |
| return state; |
| } |
| |
| public void refresh() { |
| Displays.ensureInDisplayThread(new Runnable() { |
| |
| @Override |
| public void run() { |
| if (Widgets.isAccessible(transitionAssigneesLabel)) { |
| IAtsStateDefinition toState = userSelectedTransitionToState; |
| if (toState == null) { |
| toState = AtsApiService.get().getWorkItemService().getDefaultToState(workItem); |
| } |
| if (toState == null) { |
| stateLabelLink.setText("<Not Set>"); |
| } else { |
| stateLabelLink.setText(toState.getName()); |
| } |
| stateLabelLink.getParent().layout(); |
| |
| transitionLabelLink.setEnabled(true); |
| |
| transitionAssigneesLabel.setText(workItem.getTransitionAssigneesStr()); |
| transitionAssigneesLabel.getParent().layout(); |
| |
| userSelectedTransitionToState = null; |
| } |
| } |
| }); |
| } |
| |
| private void handleChangeTransitionAssignees(AbstractWorkflowArtifact aba) { |
| userSelectedTransitionToState = getToState(); |
| if (userSelectedTransitionToState == null) { |
| OseeLog.log(Activator.class, OseeLevel.SEVERE_POPUP, "No Transition State Selected"); |
| return; |
| } |
| if (userSelectedTransitionToState.getStateType().isCompletedOrCancelledState()) { |
| AWorkbench.popup("ERROR", "No Assignees in Completed and Cancelled states"); |
| return; |
| } |
| UserCheckTreeDialog uld = new UserCheckTreeDialog(); |
| uld.setInitialSelections(aba.getTransitionAssignees()); |
| if (workItem.getParentTeamWorkflow() != null) { |
| uld.setTeamMembers(AtsApiService.get().getTeamDefinitionService().getMembersAndLeads( |
| workItem.getParentTeamWorkflow().getTeamDefinition())); |
| } |
| if (uld.open() != 0) { |
| return; |
| } |
| Collection<AtsUser> users = uld.getUsersSelected(); |
| if (users.isEmpty()) { |
| AWorkbench.popup("ERROR", "Must have at least one assignee"); |
| return; |
| } |
| workItem.setTransitionAssignees(users); |
| refresh(); |
| editor.onDirtied(); |
| } |
| |
| public boolean isSelected() { |
| return userSelectedTransitionToState != null; |
| } |
| |
| } |