Refactoring

--HG--
branch : 1.x
extra : rebase_source : 0a3659f94fbdb3fab2185bdf26c7f9838bd4995b
This commit is contained in:
Sebastien Ros
2013-01-29 12:01:44 -08:00
parent 56b656747d
commit e5c9da31c8
28 changed files with 346 additions and 282 deletions

View File

@@ -79,7 +79,7 @@ namespace Orchard.Core.Navigation.Drivers {
var selectedPath = NavigationHelper.SetSelectedPath(menuItems, routeData); var selectedPath = NavigationHelper.SetSelectedPath(menuItems, routeData);
dynamic menuShape = shapeHelper.Menu(); dynamic menuShape = shapeHelper.Menu();
if (part.Breadcrumb && selectedPath != null) { if (part.Breadcrumb && selectedPath != null) {
menuItems = selectedPath; menuItems = selectedPath;
foreach (var menuItem in menuItems) { foreach (var menuItem in menuItems) {
@@ -160,9 +160,9 @@ namespace Orchard.Core.Navigation.Drivers {
} }
} }
menuShape.MenuName(menuName); menuShape.MenuName(menuName);
menuShape.ContentItem(menu); menuShape.ContentItem(menu);
NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems); NavigationHelper.PopulateMenu(shapeHelper, menuShape, menuShape, menuItems);
return shapeHelper.Parts_MenuWidget(Menu: menuShape); return shapeHelper.Parts_MenuWidget(Menu: menuShape);

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -14,15 +14,15 @@ namespace Orchard.Workflows.Activities {
public Localizer T { get; set; } public Localizer T { get; set; }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
return true; return true;
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return GetBranches(context).Select(x => T(x)); return GetBranches(context).Select(x => T(x));
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
return GetBranches(context).Select(x => T(x)); return GetBranches(context).Select(x => T(x));
} }
@@ -44,7 +44,7 @@ namespace Orchard.Workflows.Activities {
} }
} }
private IEnumerable<string> GetBranches(ActivityContext context) { private IEnumerable<string> GetBranches(WorkflowContext context) {
if (context.State == null) { if (context.State == null) {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -15,7 +15,7 @@ namespace Orchard.Workflows.Activities {
get { return true; } get { return true; }
} }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
try { try {
string contentTypesState = context.State.ContentTypes; string contentTypesState = context.State.ContentTypes;
@@ -40,11 +40,11 @@ namespace Orchard.Workflows.Activities {
} }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return new[] { T("Done") }; return new[] { T("Done") };
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
yield return T("Done"); yield return T("Done");
} }

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -24,11 +24,11 @@ namespace Orchard.Workflows.Activities {
get { return T("Evaluates an expression."); } get { return T("Evaluates an expression."); }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return new[] { T("True"), T("False") }; return new[] { T("True"), T("False") };
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
yield return T("True"); yield return T("True");
} }
} }

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Security; using Orchard.Security;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -33,15 +33,15 @@ namespace Orchard.Workflows.Activities {
get { return "SelectRoles"; } get { return "SelectRoles"; }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return new[] {T("Yes"), T("No")}; return new[] {T("Yes"), T("No")};
} }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
return true; return true;
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
if (UserIsInRole(context)) { if (UserIsInRole(context)) {
yield return T("Yes"); yield return T("Yes");
@@ -50,7 +50,7 @@ namespace Orchard.Workflows.Activities {
yield return T("No"); yield return T("No");
} }
private bool UserIsInRole(ActivityContext context) { private bool UserIsInRole(WorkflowContext context) {
// checking if user is in an accepted role // checking if user is in an accepted role
var workContext = _workContextAccessor.GetContext(); var workContext = _workContextAccessor.GetContext();
@@ -78,7 +78,7 @@ namespace Orchard.Workflows.Activities {
return isInRole; return isInRole;
} }
private IEnumerable<string> GetRoles(ActivityContext context) { private IEnumerable<string> GetRoles(WorkflowContext context) {
if (context.State == null) { if (context.State == null) {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }

View File

@@ -8,7 +8,7 @@ using Orchard.Messaging.Services;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Security; using Orchard.Security;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -31,7 +31,7 @@ namespace Orchard.Workflows.Activities {
public Localizer T { get; set; } public Localizer T { get; set; }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return new[] { T("Sent") }; return new[] { T("Sent") };
} }
@@ -54,7 +54,7 @@ namespace Orchard.Workflows.Activities {
get { return T("Sends an e-mail to a specific user."); } get { return T("Sends an e-mail to a specific user."); }
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
string recipient = context.State.Recipient; string recipient = context.State.Recipient;
var properties = new Dictionary<string, string> { var properties = new Dictionary<string, string> {

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Tokens; using Orchard.Tokens;
using Orchard.UI.Notify; using Orchard.UI.Notify;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -35,11 +35,11 @@ namespace Orchard.Workflows.Activities {
get { return "ActivityNotify"; } get { return "ActivityNotify"; }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
yield return T("Done"); yield return T("Done");
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
string notification = context.State.Notification; string notification = context.State.Notification;
string message = context.State.Message; string message = context.State.Message;

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -14,15 +14,15 @@ namespace Orchard.Workflows.Activities {
public Localizer T { get; set; } public Localizer T { get; set; }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
return true; return true;
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
return new[] { T("Published") }; return new[] { T("Published") };
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
_contentManager.Publish(context.Content.ContentItem); _contentManager.Publish(context.Content.ContentItem);
yield return T("Published"); yield return T("Published");
} }

View File

@@ -8,19 +8,14 @@ using Orchard.Localization;
using Orchard.Services; using Orchard.Services;
using Orchard.Tasks; using Orchard.Tasks;
using Orchard.Workflows.Models; using Orchard.Workflows.Models;
using Orchard.Workflows.Models.Descriptors;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
public class TimerActivity : Event { public class TimerActivity : Event {
private readonly IClock _clock; private readonly IClock _clock;
private readonly IWorkContextAccessor _workContextAccessor;
public TimerActivity( public TimerActivity(IClock clock) {
IClock clock,
IWorkContextAccessor workContextAccessor) {
_clock = clock; _clock = clock;
_workContextAccessor = workContextAccessor;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -42,26 +37,26 @@ namespace Orchard.Workflows.Activities {
get { return "ActivityTimer"; } get { return "ActivityTimer"; }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
yield return T("Done"); yield return T("Done");
} }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
return _clock.UtcNow > When(context); return _clock.UtcNow > When(context);
} }
public override void Touch(dynamic workflowState) { public override void OnWorkflowStarted(WorkflowContext context) {
workflowState.TimerActivity_StartedUtc = _clock.UtcNow; context.WorkflowState.TimerActivity_StartedUtc = _clock.UtcNow;
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
if(_clock.UtcNow > When(context)) { if(_clock.UtcNow > When(context)) {
yield return T("Done"); yield return T("Done");
} }
} }
public static DateTime When(ActivityContext context) { public static DateTime When(WorkflowContext context) {
try { try {
int amount = context.State.Amount; int amount = context.State.Amount;
string type = context.State.Unity; string type = context.State.Unity;
@@ -122,10 +117,10 @@ namespace Orchard.Workflows.Activities {
var contentItem = _contentManager.Get(x.ContentItemRecord.Id, VersionOptions.Latest); var contentItem = _contentManager.Get(x.ContentItemRecord.Id, VersionOptions.Latest);
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State); var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
var workflowState = FormParametersHelper.FromJsonString(x.WorkflowRecord.State); var workflowState = FormParametersHelper.FromJsonString(x.WorkflowRecord.State);
return _clock.UtcNow > TimerActivity.When(new ActivityContext { return _clock.UtcNow > TimerActivity.When(new WorkflowContext {
State = state, State = state,
WorkflowState = workflowState, WorkflowState = workflowState,
Content = contentItem Content = contentItem,
}); });
}); });

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Security; using Orchard.Security;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
using Orchard.Workflows.Services; using Orchard.Workflows.Services;
namespace Orchard.Workflows.Activities { namespace Orchard.Workflows.Activities {
@@ -33,24 +33,24 @@ namespace Orchard.Workflows.Activities {
get { return "ActivityUserTask"; } get { return "ActivityUserTask"; }
} }
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) { public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context) {
foreach (var action in GetActions(context)) { foreach (var action in GetActions(context)) {
yield return T(action); yield return T(action);
} }
} }
public override bool CanExecute(ActivityContext context) { public override bool CanExecute(WorkflowContext context) {
return ActionIsValid(context) && UserIsInRole(context); return ActionIsValid(context) && UserIsInRole(context);
} }
public override IEnumerable<LocalizedString> Execute(ActivityContext context) { public override IEnumerable<LocalizedString> Execute(WorkflowContext context) {
if (ActionIsValid(context) && UserIsInRole(context)) { if (ActionIsValid(context) && UserIsInRole(context)) {
yield return T(context.Tokens["UserTask.Action"].ToString()); yield return T(context.Tokens["UserTask.Action"].ToString());
} }
} }
private bool UserIsInRole(ActivityContext context) { private bool UserIsInRole(WorkflowContext context) {
// checking if user is in an accepted role // checking if user is in an accepted role
var workContext = _workContextAccessor.GetContext(); var workContext = _workContextAccessor.GetContext();
@@ -78,7 +78,7 @@ namespace Orchard.Workflows.Activities {
return isInRole; return isInRole;
} }
private bool ActionIsValid(ActivityContext context) { private bool ActionIsValid(WorkflowContext context) {
// checking if user has triggered an accepted action // checking if user has triggered an accepted action
@@ -91,7 +91,7 @@ namespace Orchard.Workflows.Activities {
return isValidAction; return isValidAction;
} }
private IEnumerable<string> GetRoles(ActivityContext context) { private IEnumerable<string> GetRoles(WorkflowContext context) {
if (context.State == null) { if (context.State == null) {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
@@ -105,7 +105,7 @@ namespace Orchard.Workflows.Activities {
return roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList(); return roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
} }
private IEnumerable<string> GetActions(ActivityContext context) { private IEnumerable<string> GetActions(WorkflowContext context) {
if (context.State == null) { if (context.State == null) {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();

View File

@@ -0,0 +1,6 @@
namespace Orchard.Workflows.Models {
public enum ActivityState {
Excuted,
Executing
}
}

View File

@@ -0,0 +1,9 @@
namespace Orchard.Workflows.Models {
public class CancellationToken {
public void Cancel() {
IsCancelled = true;
}
public bool IsCancelled { get; private set; }
}
}

View File

@@ -1,19 +0,0 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
namespace Orchard.Workflows.Models.Descriptors {
public class ActivityContext {
public ActivityContext() {
Tokens = new Dictionary<string, object>();
}
/// <summary>
/// If set, represents the subject of the current workflow
/// </summary>
public IContent Content { get; set; }
public IDictionary<string, object> Tokens { get; set; }
public dynamic State { get; set; }
public dynamic WorkflowState { get; set; }
}
}

View File

@@ -1,14 +0,0 @@
using System;
using Orchard.Localization;
namespace Orchard.Workflows.Models.Descriptors {
public class ActivityDescriptor {
public string Category { get; set; }
public string Type { get; set; }
public LocalizedString Name { get; set; }
public LocalizedString Description { get; set; }
public Func<ActivityContext, bool> Action { get; set; }
public string Form { get; set; }
public Func<ActivityContext, LocalizedString> Display { get; set; }
}
}

View File

@@ -1,33 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Localization;
namespace Orchard.Workflows.Models.Descriptors {
public class DescribeActivityContext {
private readonly Dictionary<string, DescribeActivityFor> _describes = new Dictionary<string, DescribeActivityFor>();
public IEnumerable<TypeDescriptor<ActivityDescriptor>> Describe() {
return _describes.Select(kp => new TypeDescriptor<ActivityDescriptor> {
Category = kp.Key,
Name = kp.Value.Name,
Description = kp.Value.Description,
Descriptors = kp.Value.Types
});
}
public DescribeActivityFor For(string category) {
return For(category, null, null);
}
public DescribeActivityFor For(string category, LocalizedString name, LocalizedString description) {
DescribeActivityFor describeFor;
if (!_describes.TryGetValue(category, out describeFor)) {
describeFor = new DescribeActivityFor(category, name, description);
_describes[category] = describeFor;
}
return describeFor;
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Workflows.Models.Descriptors {
public class DescribeActivityFor {
private readonly string _category;
public DescribeActivityFor(string category, LocalizedString name, LocalizedString description) {
Types = new List<ActivityDescriptor>();
_category = category;
Name = name;
Description = description;
}
public LocalizedString Name { get; private set; }
public LocalizedString Description { get; private set; }
public List<ActivityDescriptor> Types { get; private set; }
public DescribeActivityFor Element(string type, LocalizedString name, LocalizedString description, Func<ActivityContext, bool> action, Func<ActivityContext, LocalizedString> display, string form = null) {
Types.Add(new ActivityDescriptor { Type = type, Name = name, Description = description, Category = _category, Action = action, Display = display, Form = form });
return this;
}
}
}

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
using Orchard.Localization;
namespace Orchard.Workflows.Models.Descriptors {
public class TypeDescriptor<T> {
public string Category { get; set; }
public LocalizedString Name { get; set; }
public LocalizedString Description { get; set; }
public IEnumerable<T> Descriptors { get; set; }
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Workflows.Services;
namespace Orchard.Workflows.Models {
public class WorkflowContext {
public WorkflowContext() {
Tokens = new Dictionary<string, object>();
}
/// <summary>
/// If set, represents the subject of the current workflow
/// </summary>
public IContent Content { get; set; }
public IDictionary<string, object> Tokens { get; set; }
public dynamic State { get; set; }
public dynamic WorkflowState { get; set; }
public IActivity Activity { get; set; }
public ActivityRecord Record { get; set; }
/// <summary>
/// Schedules a specific
/// </summary>
public Action<ActivityRecord> Schedule { get; set; }
public IEnumerable<TransitionRecord> GetInboundTransitions(ActivityRecord activityRecord) {
return Record.WorkflowDefinitionRecord
.TransitionRecords
.Where(transition =>
transition.DestinationActivityRecord == activityRecord
).ToArray();
}
public IEnumerable<TransitionRecord> GetOutboundTransitions(ActivityRecord activityRecord) {
return Record.WorkflowDefinitionRecord
.TransitionRecords
.Where(transition =>
transition.SourceActivityRecord == activityRecord
).ToArray();
}
public IEnumerable<TransitionRecord> GetOutboundTransitions(ActivityRecord activityRecord, LocalizedString outcome) {
return Record.WorkflowDefinitionRecord
.TransitionRecords
.Where(transition =>
transition.SourceActivityRecord == activityRecord
&& transition.SourceEndpoint == outcome.TextHint
).ToArray();
}
}
}

View File

@@ -133,14 +133,12 @@
<Compile Include="Drivers\WorkflowDriver.cs" /> <Compile Include="Drivers\WorkflowDriver.cs" />
<Compile Include="Handlers\ContentHandler.cs" /> <Compile Include="Handlers\ContentHandler.cs" />
<Compile Include="Handlers\WorkflowHandler.cs" /> <Compile Include="Handlers\WorkflowHandler.cs" />
<Compile Include="Models\ActivityState.cs" />
<Compile Include="Models\AwaitingActivityRecord.cs" /> <Compile Include="Models\AwaitingActivityRecord.cs" />
<Compile Include="Models\Descriptors\ActivityContext.cs" /> <Compile Include="Models\CancellationToken.cs" />
<Compile Include="Models\Descriptors\ActivityDescriptor.cs" /> <Compile Include="Models\WorkflowContext.cs" />
<Compile Include="Models\Descriptors\DescribeActivityContext.cs" />
<Compile Include="Models\Descriptors\DescribeActivityFor.cs" />
<Compile Include="Models\ActivityRecord.cs" /> <Compile Include="Models\ActivityRecord.cs" />
<Compile Include="Models\TransitionRecord.cs" /> <Compile Include="Models\TransitionRecord.cs" />
<Compile Include="Models\Descriptors\TypeDescriptor.cs" />
<Compile Include="Models\WorkflowRecord.cs" /> <Compile Include="Models\WorkflowRecord.cs" />
<Compile Include="Migrations.cs" /> <Compile Include="Migrations.cs" />
<Compile Include="Models\WorkflowDefinitionRecord.cs" /> <Compile Include="Models\WorkflowDefinitionRecord.cs" />
@@ -149,10 +147,9 @@
<Compile Include="Forms\UserTaskForms.cs" /> <Compile Include="Forms\UserTaskForms.cs" />
<Compile Include="Forms\NotificationActivityForms.cs" /> <Compile Include="Forms\NotificationActivityForms.cs" />
<Compile Include="ResourceManifest.cs" /> <Compile Include="ResourceManifest.cs" />
<Compile Include="Services\BaseActivity.cs" /> <Compile Include="Services\Task.cs" />
<Compile Include="Services\BlockingActivity.cs" /> <Compile Include="Services\Event.cs" />
<Compile Include="Services\IActivity.cs" /> <Compile Include="Services\IActivity.cs" />
<Compile Include="Services\IActivityProvider.cs" />
<Compile Include="Services\ActivitiesManager.cs" /> <Compile Include="Services\ActivitiesManager.cs" />
<Compile Include="Services\IActivitiesManager.cs" /> <Compile Include="Services\IActivitiesManager.cs" />
<Compile Include="Services\IWorkflowManager.cs" /> <Compile Include="Services\IWorkflowManager.cs" />

View File

@@ -1,35 +0,0 @@
using System.Collections.Generic;
using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors;
namespace Orchard.Workflows.Services {
public abstract class Task : IActivity {
public abstract string Name { get; }
public abstract LocalizedString Category { get; }
public abstract LocalizedString Description { get; }
public virtual bool IsEvent {
get { return false; }
}
public bool CanStartWorkflow { get { return false; } }
public virtual string Form {
get { return null; }
}
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
public virtual bool CanExecute(ActivityContext context) {
return true;
}
public abstract IEnumerable<LocalizedString> Execute(ActivityContext context);
public virtual void Touch(dynamic state) {
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Collections.Generic;
using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors;
namespace Orchard.Workflows.Services {
public abstract class Event : IActivity {
public abstract string Name { get; }
public abstract LocalizedString Category { get; }
public abstract LocalizedString Description { get; }
public virtual bool IsEvent {
get { return true; }
}
public virtual string Form {
get { return null; }
}
public virtual bool CanStartWorkflow {
get { return false; }
}
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
public virtual bool CanExecute(ActivityContext context) {
return true;
}
public abstract IEnumerable<LocalizedString> Execute(ActivityContext context);
public virtual void Touch(dynamic workflowState) {
}
}
}

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using Orchard.Localization;
using Orchard.Workflows.Models;
namespace Orchard.Workflows.Services {
public abstract class Event : IActivity {
public abstract string Name { get; }
public abstract LocalizedString Category { get; }
public abstract LocalizedString Description { get; }
public virtual bool IsEvent {
get { return true; }
}
public virtual string Form {
get { return null; }
}
public virtual bool CanStartWorkflow {
get { return false; }
}
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context);
public virtual bool CanExecute(WorkflowContext context) {
return true;
}
public abstract IEnumerable<LocalizedString> Execute(WorkflowContext context);
public virtual void OnWorkflowStarting(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnWorkflowStarted(WorkflowContext context) {
}
public virtual void OnWorkflowResuming(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnWorkflowResumed(WorkflowContext context) {
}
public virtual void OnActivityExecuting(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnActivityExecuted(WorkflowContext context) {
}
}
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Workflows.Models.Descriptors; using Orchard.Workflows.Models;
namespace Orchard.Workflows.Services { namespace Orchard.Workflows.Services {
public interface IActivity : IDependency { public interface IActivity : IDependency {
@@ -15,23 +15,49 @@ namespace Orchard.Workflows.Services {
/// <summary> /// <summary>
/// List of possible outcomes when the activity is executed. /// List of possible outcomes when the activity is executed.
/// </summary> /// </summary>
IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context); IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context);
/// <summary> /// <summary>
/// Whether the activity can transition to the next outcome. Can prevent the activity from being transitioned /// Whether the activity can transition to the next outcome. Can prevent the activity from being transitioned
/// because a condition is not valid. /// because a condition is not valid.
/// </summary> /// </summary>
bool CanExecute(ActivityContext context); bool CanExecute(WorkflowContext context);
/// <summary> /// <summary>
/// Executes the current activity /// Executes the current activity
/// </summary> /// </summary>
/// <returns>The names of the resulting outcomes.</returns> /// <returns>The names of the resulting outcomes.</returns>
IEnumerable<LocalizedString> Execute(ActivityContext context); IEnumerable<LocalizedString> Execute(WorkflowContext context);
/// <summary> /// <summary>
/// Called on blocking activities when they are reached /// Called on each activity when a workflow is about to start
/// </summary> /// </summary>
void Touch(dynamic workflowState); void OnWorkflowStarting(WorkflowContext context, CancellationToken cancellationToken);
/// <summary>
/// Called on each activity when a workflow has started
/// </summary>
void OnWorkflowStarted(WorkflowContext context);
/// <summary>
/// Called on each activity when a workflow is about to be resumed
/// </summary>
void OnWorkflowResuming(WorkflowContext context, CancellationToken cancellationToken);
/// <summary>
/// Called on each activity when a workflow is resumed
/// </summary>
void OnWorkflowResumed(WorkflowContext context);
/// <summary>
/// Called on each activity when an activity is about to be executed
/// </summary>
void OnActivityExecuting(WorkflowContext context, CancellationToken cancellationToken);
/// <summary>
/// Called on each activity when an activity has been executed
/// </summary>
void OnActivityExecuted(WorkflowContext context);
} }
} }

View File

@@ -1,8 +0,0 @@
using Orchard.Events;
using Orchard.Workflows.Models.Descriptors;
namespace Orchard.Workflows.Services {
public interface IActivityProvider : IEventHandler {
void Describe(DescribeActivityContext describe);
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Orchard.ContentManagement; using Orchard.ContentManagement;
using Orchard.Events; using Orchard.Events;
using Orchard.Workflows.Models;
namespace Orchard.Workflows.Services { namespace Orchard.Workflows.Services {
public interface IWorkflowManager : IEventHandler { public interface IWorkflowManager : IEventHandler {
@@ -14,8 +13,6 @@ namespace Orchard.Workflows.Services {
/// <param name="target">The <see cref="IContent"/> content item the event is related to</param> /// <param name="target">The <see cref="IContent"/> content item the event is related to</param>
/// <param name="tokensContext">An object containing the tokens context</param> /// <param name="tokensContext">An object containing the tokens context</param>
void TriggerEvent(string name, IContent target, Func<Dictionary<string, object>> tokensContext); void TriggerEvent(string name, IContent target, Func<Dictionary<string, object>> tokensContext);
IEnumerable<ActivityRecord> ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState);
} }
} }

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using Orchard.Localization;
using Orchard.Workflows.Models;
namespace Orchard.Workflows.Services {
public abstract class Task : IActivity {
public abstract string Name { get; }
public abstract LocalizedString Category { get; }
public abstract LocalizedString Description { get; }
public virtual bool IsEvent {
get { return false; }
}
public bool CanStartWorkflow { get { return false; } }
public virtual string Form {
get { return null; }
}
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext context);
public virtual bool CanExecute(WorkflowContext context) {
return true;
}
public abstract IEnumerable<LocalizedString> Execute(WorkflowContext context);
public virtual void OnWorkflowStarting(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnWorkflowStarted(WorkflowContext context) {
}
public virtual void OnWorkflowResuming(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnWorkflowResumed(WorkflowContext context) {
}
public virtual void OnActivityExecuting(WorkflowContext context, CancellationToken cancellationToken) {
}
public virtual void OnActivityExecuted(WorkflowContext context) {
}
}
}

View File

@@ -10,7 +10,6 @@ using Orchard.Workflows.Models;
using Orchard.Data; using Orchard.Data;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Tokens; using Orchard.Tokens;
using Orchard.Workflows.Models.Descriptors;
namespace Orchard.Workflows.Services { namespace Orchard.Workflows.Services {
public class WorkflowManager : IWorkflowManager { public class WorkflowManager : IWorkflowManager {
@@ -83,7 +82,12 @@ namespace Orchard.Workflows.Services {
var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized)); var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized); var state = FormParametersHelper.FromJsonString(serialized);
var workflowState = FormParametersHelper.FromJsonString(a.WorkflowRecord.State); var workflowState = FormParametersHelper.FromJsonString(a.WorkflowRecord.State);
var context = new ActivityContext { Tokens = tokens, State = state.Root, WorkflowState = workflowState, Content = target }; var context = new WorkflowContext {
Tokens = tokens,
State = state.Root,
WorkflowState = workflowState,
Content = target,
};
// check the condition // check the condition
try { try {
@@ -102,7 +106,12 @@ namespace Orchard.Workflows.Services {
var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized)); var serialized = String.IsNullOrEmpty(tokenized) ? "{}" : JsonConvert.SerializeXNode(XElement.Parse(tokenized));
var state = FormParametersHelper.FromJsonString(serialized); var state = FormParametersHelper.FromJsonString(serialized);
var workflowState = FormParametersHelper.FromJsonString("{}"); var workflowState = FormParametersHelper.FromJsonString("{}");
var context = new ActivityContext { Tokens = tokens, State = state.Root, WorkflowState = workflowState, Content = target }; var context = new WorkflowContext {
Tokens = tokens,
State = state.Root,
WorkflowState = workflowState,
Content = target,
};
// check the condition // check the condition
try { try {
@@ -125,29 +134,59 @@ namespace Orchard.Workflows.Services {
.ToList(); .ToList();
// resume halted workflows // resume halted workflows
foreach (var a in awaitingActivities) { foreach (var awaitingActivityRecord in awaitingActivities) {
ResumeWorkflow(a, target, tokens); var context = new WorkflowContext {
Activity = _activitiesManager.GetActivityByName(awaitingActivityRecord.ActivityRecord.Name),
Content = target,
Record = awaitingActivityRecord.ActivityRecord,
Tokens = tokens,
State = FormParametersHelper.FromJsonString(awaitingActivityRecord.ActivityRecord.State),
WorkflowState = FormParametersHelper.FromJsonString(awaitingActivityRecord.WorkflowRecord.State)
};
ResumeWorkflow(awaitingActivityRecord, context);
} }
// start new workflows // start new workflows
foreach (var a in startedWorkflows) { foreach (var activityRecord in startedWorkflows) {
StartWorkflow(a, target, tokens); var context = new WorkflowContext {
Activity = _activitiesManager.GetActivityByName(activityRecord.Name),
Content = target,
Record = activityRecord,
Tokens = tokens,
State = FormParametersHelper.FromJsonString(activityRecord.State),
WorkflowState = FormParametersHelper.FromJsonString("{}")
};
StartWorkflow(context);
} }
} }
private void StartWorkflow(ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens) { private void StartWorkflow(WorkflowContext context) {
var workflowState = FormParametersHelper.FromJsonString("{}");
IEnumerable<ActivityRecord> blockedOn = ExecuteWorkflow(activityRecord.WorkflowDefinitionRecord, activityRecord, target, tokens, workflowState); // signal every activity that the workflow is about to start
var cancellationToken = new CancellationToken();
InvokeActivities(context.Record.WorkflowDefinitionRecord, context, ctx => ctx.Activity.OnWorkflowStarting(ctx, cancellationToken));
if (cancellationToken.IsCancelled) {
// workflow is aborted
return;
}
// signal every activity that the workflow is has started
InvokeActivities(context.Record.WorkflowDefinitionRecord, context, ctx => ctx.Activity.OnWorkflowStarted(ctx));
var blockedOn = ExecuteWorkflow(context).ToList();
// is the workflow halted on a blocking activity ? // is the workflow halted on a blocking activity ?
if (blockedOn == null || !blockedOn.Any()) { if (!blockedOn.Any()) {
// no, nothing to do // no, nothing to do
} }
else { else {
// workflow halted, create a workflow state // workflow halted, create a workflow state
var workflow = new WorkflowRecord { var workflow = new WorkflowRecord {
WorkflowDefinitionRecord = activityRecord.WorkflowDefinitionRecord, WorkflowDefinitionRecord = context.Record.WorkflowDefinitionRecord,
State = FormParametersHelper.ToJsonString(workflowState) State = FormParametersHelper.ToJsonString(context.WorkflowState)
}; };
_workflowRepository.Create(workflow); _workflowRepository.Create(workflow);
@@ -155,54 +194,66 @@ namespace Orchard.Workflows.Services {
foreach (var blocking in blockedOn) { foreach (var blocking in blockedOn) {
workflow.AwaitingActivities.Add(new AwaitingActivityRecord { workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
ActivityRecord = blocking, ActivityRecord = blocking,
ContentItemRecord = target.ContentItem.Record ContentItemRecord = context.Content.ContentItem.Record
}); });
} }
} }
} }
private void ResumeWorkflow(AwaitingActivityRecord awaitingActivityRecord, IContent target, Dictionary<string, object> tokens) { private void ResumeWorkflow(AwaitingActivityRecord awaitingActivityRecord, WorkflowContext context) {
var workflowState = FormParametersHelper.FromJsonString(awaitingActivityRecord.WorkflowRecord.State); // signal every activity that the workflow is about to be resumed
IEnumerable<ActivityRecord> blockedOn = ExecuteWorkflow(awaitingActivityRecord.WorkflowRecord.WorkflowDefinitionRecord, awaitingActivityRecord.ActivityRecord, target, tokens, workflowState); var cancellationToken = new CancellationToken();
InvokeActivities(context.Record.WorkflowDefinitionRecord, context, ctx => ctx.Activity.OnWorkflowResuming(ctx, cancellationToken));
if (cancellationToken.IsCancelled) {
// workflow is aborted
return;
}
// signal every activity that the workflow is resumed
InvokeActivities(context.Record.WorkflowDefinitionRecord, context, ctx => ctx.Activity.OnWorkflowResumed(ctx));
var blockedOn = ExecuteWorkflow(context).ToList();
// is the workflow halted on a blocking activity ? // is the workflow halted on a blocking activity ?
if (blockedOn == null || !blockedOn.Any()) { if (!blockedOn.Any()) {
// no, delete the workflow // no, delete the workflow
_workflowRepository.Delete(awaitingActivityRecord.WorkflowRecord); _workflowRepository.Delete(awaitingActivityRecord.WorkflowRecord);
} }
else { else {
// remove all previous awaiting activities // remove all previous awaiting activities
var workflow = awaitingActivityRecord.WorkflowRecord; var workflow = awaitingActivityRecord.WorkflowRecord;
workflow.State = FormParametersHelper.ToJsonString(workflowState); workflow.State = FormParametersHelper.ToJsonString(context.WorkflowState);
workflow.AwaitingActivities.Clear(); workflow.AwaitingActivities.Clear();
// add the new ones // add the new ones
foreach (var blocking in blockedOn) { foreach (var blocking in blockedOn) {
workflow.AwaitingActivities.Add(new AwaitingActivityRecord { workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
ActivityRecord = blocking, ActivityRecord = blocking,
ContentItemRecord = target.ContentItem.Record ContentItemRecord = context.Content.ContentItem.Record
}); });
} }
} }
} }
public IEnumerable<ActivityRecord> ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState) { public IEnumerable<ActivityRecord> ExecuteWorkflow(WorkflowContext context) {
var firstPass = true; var firstPass = true;
var pending = new Stack<ActivityRecord>(); var scheduled = new Stack<ActivityRecord>();
pending.Push(activityRecord); var activityRecord = context.Record;
scheduled.Push(activityRecord);
var blocking = new List<ActivityRecord>(); var blocking = new List<ActivityRecord>();
while (pending.Any()) { while (scheduled.Any()) {
activityRecord = pending.Pop(); activityRecord = scheduled.Pop();
// while there is an activity to process // while there is an activity to process
var activity = _activitiesManager.GetActivityByName(activityRecord.Name); var activity = _activitiesManager.GetActivityByName(activityRecord.Name);
if (!firstPass){ if (!firstPass){
if(activity.IsEvent) { if(activity.IsEvent) {
activity.Touch(workflowState);
blocking.Add(activityRecord); blocking.Add(activityRecord);
continue; continue;
} }
@@ -211,17 +262,15 @@ namespace Orchard.Workflows.Services {
firstPass = false; firstPass = false;
} }
var state = FormParametersHelper.FromJsonString(activityRecord.State); var outcomes = activity.Execute(context);
var activityContext = new ActivityContext { Tokens = tokens, State = state, WorkflowState = workflowState, Content = target };
var outcomes = activity.Execute(activityContext);
if (outcomes != null) { if (outcomes != null) {
foreach (var outcome in outcomes) { foreach (var outcome in outcomes) {
// look for next activity in the graph // look for next activity in the graph
var transition = workflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint); var transition = context.Record.WorkflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint);
if (transition != null) { if (transition != null) {
pending.Push(transition.DestinationActivityRecord); scheduled.Push(transition.DestinationActivityRecord);
} }
} }
} }
@@ -230,5 +279,16 @@ namespace Orchard.Workflows.Services {
// apply Distinct() as two paths could block on the same activity // apply Distinct() as two paths could block on the same activity
return blocking.Distinct(); return blocking.Distinct();
} }
/// <summary>
/// Executes a specific action on all the activities of a workflow, using a specific context
/// </summary>
private void InvokeActivities(WorkflowDefinitionRecord workflowDefinitionRecord, WorkflowContext context, Action<WorkflowContext> action) {
foreach (var item in workflowDefinitionRecord.ActivityRecords) {
context.Activity = _activitiesManager.GetActivityByName(item.Name);
context.Record = item;
action(context);
}
}
} }
} }

View File

@@ -1,6 +1,7 @@
@using Orchard.DisplayManagement @using Orchard.DisplayManagement
@using Orchard.UI.Resources @using Orchard.UI.Resources
@using Orchard.Utility.Extensions @using Orchard.Utility.Extensions
@using Orchard.Workflows.Models
@using Orchard.Workflows.Models.Descriptors @using Orchard.Workflows.Models.Descriptors
@using Orchard.Workflows.Services @using Orchard.Workflows.Services
@@ -29,7 +30,7 @@
//<![CDATA[ //<![CDATA[
var activities = { @foreach (var activity in allActivities) { <text> var activities = { @foreach (var activity in allActivities) { <text>
'@activity.Name': { '@activity.Name': {
outcomes: [@Html.Raw(String.Join(",", activity.GetPossibleOutcomes(new ActivityContext()).Where(x => !String.IsNullOrEmpty(x.Text)).Select(x => "'" + HttpUtility.JavaScriptStringEncode(x.Text) + "'").ToArray()))], outcomes: [@Html.Raw(String.Join(",", activity.GetPossibleOutcomes(new WorkflowContext()).Where(x => !String.IsNullOrEmpty(x.Text)).Select(x => "'" + HttpUtility.JavaScriptStringEncode(x.Text) + "'").ToArray()))],
category: '@HttpUtility.JavaScriptStringEncode(activity.Category.Text)', category: '@HttpUtility.JavaScriptStringEncode(activity.Category.Text)',
description: '@HttpUtility.JavaScriptStringEncode(activity.Description.Text)', description: '@HttpUtility.JavaScriptStringEncode(activity.Description.Text)',
IsEvent: @(activity.IsEvent ? "true" : "false"), IsEvent: @(activity.IsEvent ? "true" : "false"),