public PushTransientStateNavigationTransition(Guid actionId, string name, string displayLabel, IEnumerable<Guid> sourceStateIds, WorkflowState transientTargetState, IResourceString displayTitle, IResourceString helpText) : base(actionId, name, sourceStateIds, displayTitle, helpText) { _displayLabel = displayLabel; _transientState = transientTargetState; }
/// <summary> /// This constructor has to be called from the component managing the navigation context stack. /// </summary> public NavigationContext(WorkflowState workflowState, string displayLabel, NavigationContext predecessor, IWorkflowModel workflowModel) { _workflowState = workflowState; _displayLabel = displayLabel; _predecessor = predecessor; if (workflowModel != null) { _workflowModelId = workflowModel.ModelId; _models.Add(workflowModel.ModelId, workflowModel); } }
/// <summary> /// Creates a <see cref="WorkflowAction"/> that pushes a dialog as transient state on the navigation stack. /// </summary> /// <param name="id"></param> /// <param name="name"></param> /// <param name="displayLabel"></param> /// <param name="dialogItems"></param> /// <param name="sourceState"></param> /// <param name="action"></param> /// <returns></returns> public static WorkflowAction CreateDialogMenuAction(Guid id, string name, string displayLabel, ItemsList dialogItems, WorkflowState sourceState, Action<ListItem> action) { return new PushTransientStateNavigationTransition( id, sourceState.Name + "->" + name, displayLabel, new Guid[] { sourceState.StateId }, WorkflowState.CreateTransientState(name, displayLabel, true, "ovsDialogGenericItems", false, WorkflowType.Dialog), LocalizationHelper.CreateResourceString(displayLabel)) { SortOrder = name, WorkflowNavigationContextVariables = new Dictionary<string, object> { { Constants.CONTEXT_VAR_ITEMS, dialogItems }, { Constants.CONTEXT_VAR_COMMAND, new CommandContainer<ListItem>(action) } } }; }
protected static void PushNewNavigationWorkflowState(WorkflowState newState, string navbarDisplayLabel, NavigationData newNavigationData) { IWorkflowManager workflowManager = ServiceRegistration.Get<IWorkflowManager>(); workflowManager.NavigatePushTransient(newState, new NavigationContextConfig { AdditionalContextVariables = new Dictionary<string, object> { {Consts.KEY_NAVIGATION_DATA, newNavigationData} }, NavigationContextDisplayLabel = navbarDisplayLabel }); }
// Maybe called asynchronously. protected void NavigatePushTransientInternal(WorkflowState state, NavigationContextConfig config) { EnterWriteLock("NavigatePushTransient"); try { try { if (DoPushNavigationContext(state, config)) UpdateScreen_NeedsLock(true, true); WorkflowManagerMessaging.SendNavigationCompleteMessage(); } catch (Exception e) { ServiceRegistration.Get<ILogger>().Error("WorkflowManager.NavigatePushTransientInternal: Error in workflow model or screen", e); NavigatePopInternal(1); } } finally { ExitWriteLock(); } }
/// <summary> /// Synchronous method which pushes the given workflow <paramref name="state"/> onto the navigation stack. /// </summary> /// <remarks> /// This method rethrows all exceptions which are thrown by workflow models. In case an exception is thrown, /// the internal state is still valid but the caller must pop the current navigation context from the workflow stack. /// </remarks> /// <param name="state">Workflow state to push.</param> /// <param name="config">Additional navigation context configuration.</param> /// <returns><c>true</c> if the push action was successful, else <c>false</c>.</returns> protected bool DoPushNavigationContext(WorkflowState state, NavigationContextConfig config) { if (config == null) config = EMPTY_NAVIGATION_CONTEXT_CONFIG; EnterWriteLock("DoPushNavigationContext"); try { ILogger logger = ServiceRegistration.Get<ILogger>(); NavigationContext current = CurrentNavigationContext; if (current != null && current.WorkflowState.IsTemporary && state.WorkflowType == WorkflowType.Workflow) { logger.Info("Current workflow state '{0}' is temporary, popping it from navigation stack", current.WorkflowState.Name); // The next statement can throw an exception - don't catch it - our caller should pop the current navigation context bool workflowStatePopped; DoPopNavigationContext(1, out workflowStatePopped); } NavigationContext predecessor = CurrentNavigationContext; logger.Info("WorkflowManager: Pushing workflow state '{0}' (id='{1}') onto the navigation stack...", state.Name, state.StateId); // Find non-transient state. If new state is transient, search for last non-transient state on stack. WorkflowState nonTransientState = state.IsTransient ? FindLastNonTransientState() : state; if (nonTransientState == null) { logger.Error("WorkflowManager: No non-transient state found on workflow context stack to be used as reference state. Workflow state to be pushed is '{0}' (id: '{1}')", state.Name, state.StateId); return false; } // Initialize workflow model Guid? workflowModelId = nonTransientState.WorkflowModelId; IWorkflowModel workflowModel = null; if (workflowModelId.HasValue) { object model = GetOrLoadModel(workflowModelId.Value); if (model is IWorkflowModel) { logger.Debug("WorkflowManager: Using workflow model with id '{0}' for new workflow state '{1}'", workflowModelId.Value, state.StateId); workflowModel = (IWorkflowModel) model; } else logger.Error("WorkflowManager: Model with id '{0}', which is used as workflow model in state '{1}', doesn't implement the interface '{2}'", workflowModelId.Value, state.StateId, typeof(IWorkflowModel).Name); } // Create new workflow context NavigationContext newContext = new NavigationContext(state, config.NavigationContextDisplayLabel, predecessor, workflowModel); if (config.AdditionalContextVariables != null) lock (newContext.SyncRoot) CollectionUtils.AddAll(newContext.ContextVariables, config.AdditionalContextVariables); // Check if state change is accepted bool canEnter = true; if (workflowModel != null) try { canEnter = workflowModel.CanEnterState(predecessor, newContext); } catch (Exception e) { logger.Error("WorkflowManager: Error checking if workflow model '{0}' can enter workflow state '{1}'", e, workflowModel.ModelId, newContext.WorkflowState.StateId); canEnter = false; } if (!canEnter) { logger.Debug("WorkflowManager: Workflow model with id '{0}' doesn't accept the state being pushed onto the workflow context stack. Reverting to old workflow state.", workflowModelId); return false; } // Store model exceptions IList<Exception> delayedExceptions = new List<Exception>(); // Push new context logger.Debug("WorkflowManager: Entering workflow state '{0}'", state.Name); _navigationContextStack.Push(newContext); Guid? predecessorModelId = predecessor == null ? null : predecessor.WorkflowModelId; // Communicate context change to models bool modelChange = workflowModelId != predecessorModelId; // - Handle predecessor workflow model IWorkflowModel predecessorWorkflowModel = predecessorModelId.HasValue ? GetOrLoadModel(predecessorModelId.Value) as IWorkflowModel : null; if (predecessorWorkflowModel != null) if (modelChange) { logger.Debug("WorkflowManager: Deactivating predecessor workflow model '{0}'", predecessorModelId.Value); try { predecessorWorkflowModel.Deactivate(predecessor, newContext); } catch (Exception e) { logger.Error("WorkflowManager: Error deactivating workflow model '{0}'", e, predecessorWorkflowModel.ModelId); delayedExceptions.Add(e); } } // else: same model is currently active - model context change will be handled in the next block // - Handle new workflow model if (workflowModel != null) if (modelChange) { if (predecessor == null) logger.Debug("WorkflowManager: Starting first model context with workflow state '{0}' in workflow model '{1}'", newContext.WorkflowState.StateId, workflowModelId.Value); else logger.Debug("WorkflowManager: Entering model context with workflow state '{0}' (old state was '{1}') in new workflow model '{2}'", newContext.WorkflowState.StateId, predecessor.WorkflowState.StateId, workflowModelId.Value); try { workflowModel.EnterModelContext(predecessor, newContext); } catch (Exception e) { logger.Error("WorkflowManager: Error entering model context of workflow model '{0}' for workflow state '{1}'", e, workflowModel.ModelId, newContext.WorkflowState.StateId); delayedExceptions.Add(e); } } else { logger.Debug("WorkflowManager: Changing model context to workflow state '{0}' (old state was '{1}') in workflow model '{2}'", newContext.WorkflowState.StateId, predecessor == null ? null : predecessor.WorkflowState.StateId.ToString(), workflowModelId.Value); try { workflowModel.ChangeModelContext(predecessor, newContext, true); } catch (Exception e) { logger.Error("WorkflowManager: Error changing model context of workflow model '{0}' from workflow state '{1}' to workflow state '{2}'", e, workflowModel.ModelId, predecessor.WorkflowState.StateId, newContext.WorkflowState.StateId); delayedExceptions.Add(e); } } if (state.WorkflowType == WorkflowType.Workflow) { // Compile menu actions logger.Debug("WorkflowManager: Compiling menu actions for workflow state '{0}'", state.Name); IDictionary<Guid, WorkflowAction> menuActions = new Dictionary<Guid, WorkflowAction>(); foreach (WorkflowAction action in FilterActionsBySourceState(state.StateId, _menuActions.Values)) menuActions.Add(action.ActionId, action); if (workflowModel != null) try { workflowModel.UpdateMenuActions(newContext, menuActions); } catch (Exception e) { logger.Error("WorkflowManager: Error updating menu actions in workflow model '{0}' for workflow state '{1}'", e, workflowModel.ModelId, newContext.WorkflowState.StateId); delayedExceptions.Add(e); } newContext.SetMenuActions(menuActions.Values); } if (delayedExceptions.Count > 0) throw delayedExceptions.First(); WorkflowManagerMessaging.SendStatePushedMessage(newContext); return true; } finally { ExitWriteLock(); IterateCache_NoLock(); } }
public void NavigatePushTransientAsync(WorkflowState state, NavigationContextConfig config) { IThreadPool threadPool = ServiceRegistration.Get<IThreadPool>(); threadPool.Add(() => NavigatePushTransientInternal(state, config)); }
public void NavigatePushTransient(WorkflowState state, NavigationContextConfig config) { NavigatePushTransientInternal(state, config); }
public PushTransientStateNavigationTransition(Guid actionId, string name, string displayLabel, IEnumerable<Guid> sourceStateIds, WorkflowState transientTargetState, IResourceString displayTitle) : this(actionId, name, displayLabel, sourceStateIds, transientTargetState, displayTitle, null) { }
public PushTransientStateNavigationTransition(Guid actionId, string name, string displayLabel, IEnumerable <Guid> sourceStateIds, WorkflowState transientTargetState, IResourceString displayTitle) : this(actionId, name, displayLabel, sourceStateIds, transientTargetState, displayTitle, null) { }