/// <summary> /// Initializes a new instance of the <see cref="WorkflowResult"/> class. /// </summary> /// <param name="exitState">The exit state.</param> /// <param name="exitAssignee">The exit assignee.</param> /// <param name="exitAssigneeType">The exit assignee type.</param> public WorkflowResult(ItemState exitState, string exitAssignee = null, ComparisonType? exitAssigneeType = null) { this.ExitState = exitState; this.ExitAssignee = exitAssignee; this.ExitAssigneeType = exitAssigneeType; }
/// <summary> /// Find all eligible applications and perform system transitions. /// </summary> /// <param name="session">A secure session.</param> public void DoWork(SecureSession session) { List<SystemTransitionWorkflowStateList> stateTransitionList = this.GetStateTransitions(session); foreach (var stateList in stateTransitionList) { foreach (WorkflowState state in stateList) { UnitOfWorkList<ApplicationWorkItem> work = this.FindWork(session, stateList.FormId, state); foreach (UnitOfWork<ApplicationWorkItem> workItem in work) { ApplicationWorkflowItem workflowItem = this.GetWorkflowItem(session, workItem); if (!string.IsNullOrWhiteSpace(workItem.Item.EmailAddress)) { workflowItem.UserEmail = workItem.Item.EmailAddress; workflowItem.UserId = workItem.Item.UserId; } else { workflowItem.UserId = session.AuthenticatedUser.Id; } ItemState oldState = new ItemState(workItem.Item.Application.WorkflowState); WorkflowResult result = this.workflowService.Trigger(workflowItem, workItem.Item.Transition, oldState); /* if (workItem.Item.Transition.Trigger == TriggerEvent.EmailLink) { this.MarkWorkflowActionProcessed(); } */ if (result == null || result.ExitState.Name == oldState.Name) { continue; } this.formServiceGateway.SubmitApplication(session, workItem.Item.Application, result.ExitState.Name, result.ExitAssignee, result.ExitAssigneeType); this.workflowService.LogStateChange(workflowItem, oldState, result.ExitState); } } /* // When paginating... work = this.FindWork(session, stateList.FormId, state); while (work.Count > 0) { // Get more work if available work = this.FindWork(session, stateList.FormId, state); } */ } }
/// <summary> /// Performs workflow on behalf of the submit. /// </summary> /// <param name="secureSession">The session data.</param> /// <param name="application">The application to perform workflow for.</param> /// <param name="existingApplicationData">The existing application data.</param> /// <param name="product">The product definition.</param> /// <param name="scopedPageList">The list of pages required to process the application in its current state.</param> /// <param name="currentItemState">The current workflow state.</param> /// <param name="workflowState">The new / requested workflow state.</param> /// <param name="assignee">The assignee value from the workflow transition.</param> /// <param name="assigneeType">The assignee type from the workflow transition.</param> /// <param name="newState">After a successful transition, will be the new workflow state, otherwise <see langword="null"/>.</param> /// <returns>After a successful transition, the workflow item, otherwise <see langword="null"/>.</returns> private ApplicationWorkflowItem PerformWorkflow(SecureSession secureSession, Application application, ApplicationData existingApplicationData, ProductDefinition product, PageList scopedPageList, ItemState currentItemState, string workflowState, string assignee, ComparisonType? assigneeType, out ItemState newState) { newState = currentItemState; WorkflowConfigurationContainer workflowContainer = this.workflowService.GetWorkflowConfiguration(WorkflowTargetType.FormApplication, application.FormId, product.Version); WorkflowState state = workflowContainer.Configuration.States.FirstOrDefault(s => s.Name == currentItemState.Name); WorkflowTransition transition = (state == null) ? null : string.IsNullOrWhiteSpace(workflowState) ? null : state.Transitions.FirstOrDefault(t => t.Trigger != TriggerEvent.Submit && t.ExitStateName == workflowState); Organisation entitleTo; ApplicationWorkflowItem workflowItem = new ApplicationWorkflowItem(application, existingApplicationData, scopedPageList.AllControls) { FormVersion = product.Version }; // Do non-submit transition. Only Service accounts can perform this operation. if (transition != null && secureSession.AuthenticatedUser != null && secureSession.AuthenticatedUser.AccountType == AccountType.Service) { User assignTo = this.DetermineAssignee(assignee, assigneeType, application); application.WorkflowState = transition.ExitStateName; application.StateTransitionDate = DateTime.Now; application.AssignedTo = (assignTo != null) ? assignTo.Id : null; application.AssignedToDisplayName = (assignTo != null) ? assignTo.DisplayName : null; entitleTo = this.DetermineOrganisation(transition.ExitOrganisation, application); application.OrganisationId = (entitleTo != null) ? entitleTo.Id : application.OrganisationId; application.OrganisationName = (entitleTo != null) ? entitleTo.Name : application.OrganisationName; return workflowItem; } // Do submit transition. WorkflowResult result = this.workflowService.Trigger(workflowItem, new WorkflowTransition { Trigger = TriggerEvent.Submit }, currentItemState); // If result = null, there were NO valid transitions, so we leave the application essentially as is, but unassign it. if (result == null) { application.AssignedTo = null; application.AssignedToDisplayName = null; } if (result != null) { newState = result.ExitState; if (newState != null && string.Compare(newState.Name, application.WorkflowState, StringComparison.OrdinalIgnoreCase) != 0) { application.StateTransitionDate = DateTime.Now; } application.WorkflowState = (newState == null) ? null : newState.Name; User user = this.DetermineAssignee(result.ExitAssignee, result.ExitAssigneeType, application); application.AssignedTo = (user != null) ? user.Id : null; application.AssignedToDisplayName = (user != null) ? user.DisplayName : null; entitleTo = this.DetermineOrganisation(result.ExitOrganisation, application); application.OrganisationId = (entitleTo != null) ? entitleTo.Id : application.OrganisationId; application.OrganisationName = (entitleTo != null) ? entitleTo.Name : application.OrganisationName; } return workflowItem; }
/// <summary> /// Saves an application following successful validation and workflow processing. /// </summary> /// <param name="sessionData">The session data.</param> /// <param name="application">The <see cref="Application" /> to save.</param> /// <param name="workflowState">For a manual transition, the desired state to transition to.</param> /// <param name="assignee">The assignee value from the workflow transition.</param> /// <param name="assigneeType">The assignee type from the workflow transition.</param> /// <param name="submitPageId">The id of the page that triggered the form submission.</param> /// <returns>The saved <see cref="Application" />.</returns> public Application SubmitApplication(SessionData sessionData, Application application, string workflowState = null, string assignee = null, ComparisonType? assigneeType = null, int? submitPageId = null) { User user = this.userManager.GetUserById(sessionData.UserId); SecureSession secureSession = new SecureSession(sessionData.DeserializationSource, sessionData.SessionId, user); ProductDefinition product = this.UpdateApplicationMetadata(secureSession, application); application.Draft = false; submitPageId = submitPageId ?? product.FormDefinition.Pages[0].PageId; PageList previousPages = product.FormDefinition.Pages.GetRange(submitPageId.Value); var rolesList = this.DataAccess.GetRoleList(); AccessLevel applicationAccess = this.entitlementProvider.GetApplicationAccess(secureSession, application, rolesList, product.Version); if (applicationAccess != AccessLevel.Write) { throw new SecurityException(ExceptionMessages.UnauthorisedApplicationSave); } Application existingApplication = application.IsNew ? null : this.GetApplication(application.ApplicationId); ApplicationData existingApplicationData = existingApplication == null ? null : existingApplication.ApplicationData; if (existingApplication != null) { application.ApplicationIdDisplay = existingApplication.ApplicationIdDisplay; } List<PageAccess> pagesAccess = this.entitlementProvider.GetPagesAccess(secureSession, application, previousPages, rolesList, product.Version); // For validation, calculations and workflow execution we need only the controls that resides in previous pages and are accessible under the current application state List<Page> previousAccessiblePages = previousPages.Where(p => pagesAccess.Any(e => e.Id == p.PageId && e.AccessLevel >= AccessLevel.Read)).ToList(); PageList scopedPages = new PageList(previousAccessiblePages); ControlList scopedControls = scopedPages.AllControls; List<ControlAccess> controlsAccess = this.entitlementProvider.GetControlsAccess(secureSession, application, scopedControls, rolesList, product.Version); if (user == null || user.AccountType != AccountType.Service) { ValidationResults validationResults = this.ValidateApplication(application, scopedPages, scopedControls, controlsAccess); if (!validationResults.IsValid) { throw new ValidationException(validationResults); } } this.VerifyApplicationDataIntegrity(application, existingApplicationData, scopedControls, controlsAccess); this.RunCalculations(application, scopedPages.AllControls); // Workflow ItemState currentItemState = new ItemState(application.WorkflowState); ItemState newState; ApplicationWorkflowItem workflowItem = this.PerformWorkflow(secureSession, application, existingApplicationData, product, scopedPages, currentItemState, workflowState, assignee, assigneeType, out newState); // Save application. Application savedApplication = this.DataAccess.SaveApplication(application); this.LogWorkflow(secureSession, savedApplication, application.IsNew, workflowItem, currentItemState, newState); return savedApplication; }
/// <summary> /// Logs workflow state changes. /// </summary> /// <param name="secureSession">The session data.</param> /// <param name="application">The saved application.</param> /// <param name="isNew">A value indicating whether the application is new.</param> /// <param name="workflowItem">The workflow item to log.</param> /// <param name="previousState">The previous state of <paramref name="application"/>.</param> /// <param name="newState">The new state of <paramref name="application"/>.</param> private void LogWorkflow(SecureSession secureSession, Application application, bool isNew, ApplicationWorkflowItem workflowItem, ItemState previousState, ItemState newState) { if (workflowItem == null && !isNew) { return; } workflowItem = workflowItem ?? new ApplicationWorkflowItem { FormId = application.FormId, UserId = secureSession.AuthenticatedUser != null ? secureSession.AuthenticatedUser.Id : null }; workflowItem.ApplicationId = application.ApplicationId; workflowItem.VersionNumber = application.VersionNumber; workflowItem.PostedData = application.ApplicationData; if (isNew && newState != null && previousState.Name != newState.Name) { // Special case to log from blank to the first state when we are moving straight into the next state. this.workflowService.LogStateChange(workflowItem, new ItemState(null), previousState); } if (isNew && (newState == null || previousState.Name == newState.Name)) { newState = previousState; previousState = new ItemState(null); } this.workflowService.LogStateChange(workflowItem, previousState, newState); }