mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Engine working
--HG-- branch : 1.x extra : rebase_source : eee366cd0694d27ac015129ac82d4dc702a19c5f
This commit is contained in:
@@ -12,21 +12,26 @@ namespace Orchard.Workflows.Activities {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override bool CanExecute(ActivityContext context) {
|
||||
string contenttypes = context.State.ContentTypes;
|
||||
var content = context.Tokens["Content"] as IContent;
|
||||
try {
|
||||
string contenttypes = context.State.ContentTypes;
|
||||
var content = context.Tokens["Content"] as IContent;
|
||||
|
||||
// "" means 'any'
|
||||
if (String.IsNullOrEmpty(contenttypes)) {
|
||||
return true;
|
||||
// "" means 'any'
|
||||
if (String.IsNullOrEmpty(contenttypes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentTypes = contenttypes.Split(new[] {','});
|
||||
|
||||
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
|
||||
}
|
||||
|
||||
if (content == null) {
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentTypes = contenttypes.Split(new[] { ',' });
|
||||
|
||||
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
@@ -34,7 +39,13 @@ namespace Orchard.Workflows.Activities {
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
return T("True");
|
||||
return T("Success");
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
get {
|
||||
return "SelectContentTypes";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -146,6 +146,21 @@ namespace Orchard.Workflows.Controllers {
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var workflowDefinition = _workflowDefinitionRecords.Get(id);
|
||||
|
||||
if (workflowDefinition != null) {
|
||||
_workflowDefinitionRecords.Delete(workflowDefinition);
|
||||
Services.Notifier.Information(T("Workflow {0} deleted", workflowDefinition.Name));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private WorkflowDefinitionViewModel CreateWorkflowDefinitionViewModel(WorkflowDefinitionRecord workflowDefinitionRecord) {
|
||||
if (workflowDefinitionRecord == null) {
|
||||
throw new ArgumentNullException("workflowDefinitionRecord");
|
||||
@@ -162,6 +177,7 @@ namespace Orchard.Workflows.Controllers {
|
||||
activity.ClientId = x.Name + "_" + x.Id;
|
||||
activity.Left = x.X;
|
||||
activity.Top = x.Y;
|
||||
activity.Start = x.Start;
|
||||
activity.State = FormParametersHelper.FromJsonString(x.State);
|
||||
|
||||
return activity;
|
||||
@@ -193,6 +209,8 @@ namespace Orchard.Workflows.Controllers {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
workflowDefinitionRecord.Enabled = true;
|
||||
|
||||
var state = FormParametersHelper.FromJsonString(data);
|
||||
var activitiesIndex = new Dictionary<string, ActivityRecord>();
|
||||
|
||||
@@ -205,6 +223,7 @@ namespace Orchard.Workflows.Controllers {
|
||||
Name = activity.Name,
|
||||
X = activity.Left,
|
||||
Y = activity.Top,
|
||||
Start = activity.Start,
|
||||
State = FormParametersHelper.ToJsonString(activity.State),
|
||||
WorkflowDefinitionRecord = workflowDefinitionRecord
|
||||
});
|
||||
|
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Handlers {
|
||||
|
||||
public class RulePartHandler : ContentHandler {
|
||||
public RulePartHandler(IWorkflowManager workflowManager) {
|
||||
|
||||
OnPublished<ContentPart>(
|
||||
(context, part) =>
|
||||
workflowManager.TriggerEvent("ContentPublished",
|
||||
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
|
||||
|
||||
OnRemoved<ContentPart>(
|
||||
(context, part) =>
|
||||
workflowManager.TriggerEvent("ContentRemoved",
|
||||
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
|
||||
|
||||
OnVersioned<ContentPart>(
|
||||
(context, part1, part2) =>
|
||||
workflowManager.TriggerEvent("ContentVersioned",
|
||||
() => new Dictionary<string, object> { { "Content", part1.ContentItem } }));
|
||||
|
||||
OnCreated<ContentPart>(
|
||||
(context, part) =>
|
||||
workflowManager.TriggerEvent("ContentCreated",
|
||||
() => new Dictionary<string, object> { { "Content", context.ContentItem } }));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -32,7 +32,8 @@ namespace Orchard.Workflows {
|
||||
SchemaBuilder.CreateTable("AwaitingActivityRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<int>("ActivityRecord_id")
|
||||
);
|
||||
.Column<int>("WorkflowRecord_id")
|
||||
);
|
||||
|
||||
// Creating table ActivityRecord
|
||||
SchemaBuilder.CreateTable("ActivityRecord", table => table
|
||||
@@ -41,10 +42,12 @@ namespace Orchard.Workflows {
|
||||
.Column<int>("X")
|
||||
.Column<int>("Y")
|
||||
.Column<string>("State", c => c.Unlimited())
|
||||
.Column<bool>("Start")
|
||||
.Column<int>("WorkflowDefinitionRecord_id")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -28,6 +28,11 @@ namespace Orchard.Workflows.Models {
|
||||
/// </summary>
|
||||
public virtual int Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the activity is a start state for a WorkflowDefinition.
|
||||
/// </summary>
|
||||
public virtual bool Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="WorkflowDefinitionRecord"/>
|
||||
/// containing this activity.
|
||||
|
@@ -7,6 +7,6 @@ namespace Orchard.Workflows.Models {
|
||||
public virtual ActivityRecord ActivityRecord { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual IList<WorkflowRecord> WorkflowRecords { get; set; }
|
||||
public virtual WorkflowRecord WorkflowRecord { get; set; }
|
||||
}
|
||||
}
|
@@ -72,6 +72,7 @@
|
||||
<Content Include="Scripts\orchard-workflows-serialize.js" />
|
||||
<Content Include="Scripts\orchard-workflows.js" />
|
||||
<Content Include="Styles\admin-workflows.css" />
|
||||
<Content Include="Styles\workflows-activity-decision.css" />
|
||||
<Content Include="Styles\images\blocking.png" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
@@ -104,6 +105,7 @@
|
||||
<Compile Include="Activities\NotificationActivity.cs" />
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Handlers\ContentHandler.cs" />
|
||||
<Compile Include="Models\AwaitingActivityRecord.cs" />
|
||||
<Compile Include="Models\Descriptors\ActivityContext.cs" />
|
||||
<Compile Include="Models\Descriptors\ActivityDescriptor.cs" />
|
||||
@@ -125,13 +127,13 @@
|
||||
<Compile Include="Services\IActivityProvider.cs" />
|
||||
<Compile Include="Services\ActivitiesManager.cs" />
|
||||
<Compile Include="Services\IActivitiesManager.cs" />
|
||||
<Compile Include="Services\IWorkflowManager.cs" />
|
||||
<Compile Include="Services\WorkflowManager.cs" />
|
||||
<Compile Include="ViewModels\AdminEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\AdminIndexViewModel.cs" />
|
||||
<Compile Include="ViewModels\WorkflowDefinitionViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Handlers\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
@@ -156,6 +158,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-Notify.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-Decision.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
@@ -27,7 +27,7 @@ namespace Orchard.Workflows.Providers {
|
||||
var f = Shape.Form(
|
||||
Id: "AnyOfContentTypes",
|
||||
_Parts: Shape.SelectList(
|
||||
Id: "contenttypes", Name: "contenttypes",
|
||||
Id: "contenttypes", Name: "ContentTypes",
|
||||
Title: T("Content types"),
|
||||
Description: T("Select some content types."),
|
||||
Size: 10,
|
||||
|
@@ -12,6 +12,7 @@ var saveLocal = function (localId) {
|
||||
workflow.Activities.push({
|
||||
Name: activity.viewModel.name,
|
||||
ClientId: activity.viewModel.clientId,
|
||||
Start: activity.viewModel.start,
|
||||
State: activity.viewModel.state,
|
||||
Left: $(activity).position().left,
|
||||
Top: $(activity).position().top
|
||||
@@ -63,7 +64,7 @@ var loadActivities = function (localId) {
|
||||
// activities
|
||||
for (var i = 0; i < workflow.Activities.length; i++) {
|
||||
var activity = workflow.Activities[i];
|
||||
renderActivity(activity.ClientId, activity.Name, activity.State, activity.Top, activity.Left);
|
||||
renderActivity(activity.ClientId, activity.Name, activity.State, activity.Start, activity.Top, activity.Left);
|
||||
}
|
||||
|
||||
// connections
|
||||
|
@@ -1,19 +1,19 @@
|
||||
var connectorPaintStyle = {
|
||||
lineWidth: 3,
|
||||
strokeStyle: "grey",
|
||||
lineWidth: 2,
|
||||
strokeStyle: "#999",
|
||||
joinstyle: "round",
|
||||
//outlineColor: "white",
|
||||
//outlineWidth: 7
|
||||
};
|
||||
|
||||
var connectorHoverStyle = {
|
||||
lineWidth: 3,
|
||||
strokeStyle: "#2e2aF8"
|
||||
lineWidth: 2,
|
||||
strokeStyle: "#225588"
|
||||
};
|
||||
|
||||
var sourceEndpointOptions = {
|
||||
endpoint: "Dot",
|
||||
paintStyle: { fillStyle: "#225588", radius: 7 },
|
||||
endpoint: ["Dot", { cssClass: 'sourceEndpoint', radius: 5 }],
|
||||
paintStyle: { fillStyle: '#225588' },
|
||||
isSource: true,
|
||||
isTarget: false,
|
||||
connector: ["Flowchart"], // gap needs to be the same as makeTarget.paintStyle.radius
|
||||
@@ -48,6 +48,9 @@
|
||||
// deserialize the previously locally saved workflow
|
||||
loadActivities(localId);
|
||||
|
||||
// create activity overlays for controlling their states
|
||||
createOverlays();
|
||||
|
||||
// a new connection is created
|
||||
jsPlumb.bind("jsPlumbConnection", function (connectionInfo) {
|
||||
// ...update your data model here. The contents of the 'connectionInfo' are described below.
|
||||
@@ -63,7 +66,7 @@
|
||||
|
||||
// instanciates a new workflow widget in the editor
|
||||
var createActivity = function (activityName) {
|
||||
renderActivity(null, activityName, {}, 10, 10);
|
||||
renderActivity(null, activityName, {}, false, 10, 10);
|
||||
};
|
||||
|
||||
|
||||
@@ -74,7 +77,7 @@
|
||||
createActivity(activityName);
|
||||
});
|
||||
|
||||
var renderActivity = function (clientId, name, state, top, left) {
|
||||
var renderActivity = function (clientId, name, state, start, top, left) {
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
@@ -90,6 +93,10 @@
|
||||
|
||||
dom.addClass('activity');
|
||||
|
||||
if (start) {
|
||||
dom.addClass('start');
|
||||
}
|
||||
|
||||
if (clientId) {
|
||||
dom.attr('id', clientId);
|
||||
}
|
||||
@@ -97,7 +104,7 @@
|
||||
var editor = $('#activity-editor');
|
||||
editor.append(dom);
|
||||
|
||||
jsPlumb.draggable(dom, { containment: "parent", scroll: true });
|
||||
jsPlumb.draggable(dom, { containment: "parent", scroll: true, drag: function () { hideOverlay(true); } });
|
||||
|
||||
jsPlumb.makeTarget(dom, {
|
||||
dropOptions: { hoverClass: "dragHover" },
|
||||
@@ -110,6 +117,7 @@
|
||||
elt.viewModel = {
|
||||
name: name,
|
||||
state: state,
|
||||
start: start,
|
||||
clientId: dom.attr("id"),
|
||||
};
|
||||
|
||||
@@ -143,6 +151,78 @@
|
||||
|
||||
};
|
||||
|
||||
var onActivity = false;
|
||||
var onOverlay = false;
|
||||
|
||||
$("#initial-state").button();
|
||||
|
||||
var createOverlays = function () {
|
||||
var events = $('.activity.blocking');
|
||||
|
||||
$('#activity-overlay').hover(
|
||||
function () {
|
||||
onOverlay = true;
|
||||
},
|
||||
function () {
|
||||
onOverlay = false;
|
||||
setTimeout(hideOverlay, 50);
|
||||
});
|
||||
|
||||
|
||||
hideOverlay();
|
||||
events.hover(
|
||||
// mouse enter
|
||||
function () {
|
||||
var self = $(this);
|
||||
var overlay = $('#activity-overlay');
|
||||
|
||||
overlay.position({
|
||||
my: "right bottom",
|
||||
at: "right top",
|
||||
offset: "10 10",
|
||||
of: self, // or $("#otherdiv)
|
||||
collision: "none"
|
||||
});
|
||||
|
||||
onActivity = true;
|
||||
|
||||
if (!overlay.is(":visible")) {
|
||||
var state = $("#initial-state");
|
||||
state.prop('checked', self.hasClass('start')).button("refresh");
|
||||
|
||||
state.unbind("click").click(function () {
|
||||
updateStart(self, state.is(":checked"));
|
||||
});
|
||||
|
||||
overlay.show();
|
||||
}
|
||||
},
|
||||
// mouse leave
|
||||
function () {
|
||||
onActivity = false;
|
||||
setTimeout(hideOverlay, 50);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
var hideOverlay = function (force) {
|
||||
if (force || (!onOverlay && !onActivity)) {
|
||||
var overlay = $('#activity-overlay');
|
||||
overlay.offset({ top: 0, left: 0 });
|
||||
overlay.hide();
|
||||
|
||||
onOverlay = false;
|
||||
onActivity = false;
|
||||
}
|
||||
};
|
||||
|
||||
var updateStart = function(dom, isStart) {
|
||||
var clientId = $(dom).attr('id');
|
||||
var activity = getActivity(localId, clientId);
|
||||
|
||||
$(dom).get(0).viewModel.start = isStart;
|
||||
|
||||
$(dom).toggleClass('start', isStart);
|
||||
};
|
||||
|
||||
|
||||
|
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Events;
|
||||
using Orchard.Workflows.Models;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public interface IWorkflowManager : IEventHandler {
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a specific Event, and provides the tokens context if the event is
|
||||
/// actually executed
|
||||
/// </summary>
|
||||
/// <param name="name">The type of the event to trigger, e.g. Publish</param>
|
||||
/// <param name="tokensContext">An object containing the tokens context</param>
|
||||
void TriggerEvent(string name, Func<Dictionary<string, object>> tokensContext);
|
||||
|
||||
ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, Dictionary<string, object> tokens);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,202 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Workflows.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public class WorkflowManager : IWorkflowManager {
|
||||
private readonly IActivitiesManager _activitiesManager;
|
||||
private readonly IRepository<ActivityRecord> _activityRepository;
|
||||
private readonly IRepository<WorkflowRecord> _workflowRepository;
|
||||
private readonly IRepository<AwaitingActivityRecord> _awaitingActivityRepository;
|
||||
private readonly IRepository<WorkflowDefinitionRecord> _workflowDefinitionRepository;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public WorkflowManager(
|
||||
IActivitiesManager activitiesManager,
|
||||
IRepository<ActivityRecord> activityRepository,
|
||||
IRepository<WorkflowRecord> workflowRepository,
|
||||
IRepository<AwaitingActivityRecord> awaitingActivityRepository,
|
||||
IRepository<WorkflowDefinitionRecord> workflowDefinitionRepository,
|
||||
ITokenizer tokenizer) {
|
||||
_activitiesManager = activitiesManager;
|
||||
_activityRepository = activityRepository;
|
||||
_workflowRepository = workflowRepository;
|
||||
_awaitingActivityRepository = awaitingActivityRepository;
|
||||
_workflowDefinitionRepository = workflowDefinitionRepository;
|
||||
_tokenizer = tokenizer;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void TriggerEvent(string name, Func<Dictionary<string, object>> tokensContext) {
|
||||
var tokens = tokensContext();
|
||||
|
||||
var activity = _activitiesManager.GetActivityByName(name);
|
||||
|
||||
if (activity == null) {
|
||||
Logger.Error("Activity {0} was not found", name);
|
||||
return;
|
||||
}
|
||||
|
||||
var startedWorkflows = new List<ActivityRecord>();
|
||||
|
||||
// look for workflow definitions with a corresponding starting activity,
|
||||
startedWorkflows.AddRange(_activityRepository.Table.Where(
|
||||
x =>x.Name == name && x.Start && x.WorkflowDefinitionRecord.Enabled
|
||||
)
|
||||
);
|
||||
|
||||
var awaitingActivities = new List<AwaitingActivityRecord>();
|
||||
|
||||
// and any running workflow paused on this kind of activity
|
||||
awaitingActivities.AddRange(_awaitingActivityRepository.Table.Where(
|
||||
x => x.ActivityRecord.Name == name && x.ActivityRecord.Start == false
|
||||
).ToList()
|
||||
);
|
||||
|
||||
// if no activity record is matching the event, do nothing
|
||||
if (!startedWorkflows.Any() && !awaitingActivities.Any()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// evaluate processing condition
|
||||
awaitingActivities = awaitingActivities.Where(a => {
|
||||
var formatted = JsonConvert.DeserializeXNode(a.ActivityRecord.State).ToString();
|
||||
var tokenized = _tokenizer.Replace(formatted, tokens);
|
||||
var serialized = JsonConvert.SerializeXNode(XElement.Parse(tokenized));
|
||||
var state = FormParametersHelper.FromJsonString(serialized);
|
||||
var context = new ActivityContext { Tokens = tokens, State = state };
|
||||
|
||||
// check the condition
|
||||
try {
|
||||
return activity.CanExecute(context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error("Error while evaluating an activity condition on {0}: {1}", name, e.ToString());
|
||||
return false;
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
// evaluate processing condition
|
||||
startedWorkflows = startedWorkflows.Where(a => {
|
||||
var formatted = JsonConvert.DeserializeXNode(a.State).ToString();
|
||||
var tokenized = _tokenizer.Replace(formatted, tokens);
|
||||
var serialized = JsonConvert.SerializeXNode(XElement.Parse(tokenized));
|
||||
var state = FormParametersHelper.FromJsonString(serialized);
|
||||
var context = new ActivityContext { Tokens = tokens, State = state };
|
||||
|
||||
// check the condition
|
||||
try {
|
||||
return activity.CanExecute(context);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error("Error while evaluating an activity condition on {0}: {1}", name, e.ToString());
|
||||
return false;
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
// if no activity record is matching the event, do nothing
|
||||
if (!startedWorkflows.Any() && !awaitingActivities.Any()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load workflow definitions too for eager loading
|
||||
_workflowDefinitionRepository.Table
|
||||
.Where(x => x.Enabled && x.ActivityRecords.Any(e => e.Name == name))
|
||||
.ToList();
|
||||
|
||||
// resume halted workflows
|
||||
foreach (var a in awaitingActivities) {
|
||||
ResumeWorkflow(a, tokens);
|
||||
}
|
||||
|
||||
// start new workflows
|
||||
foreach (var a in startedWorkflows) {
|
||||
StartWorkflow(a, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartWorkflow(ActivityRecord activityRecord, Dictionary<string, object> tokens) {
|
||||
var lastActivity = ExecuteWorkflow(activityRecord.WorkflowDefinitionRecord, activityRecord, tokens);
|
||||
|
||||
// is the workflow halted on a blocking activity ?
|
||||
if (lastActivity == null) {
|
||||
// no, nothing to do
|
||||
}
|
||||
else {
|
||||
// workflow halted, create a workflow state
|
||||
var workflow = new WorkflowRecord {
|
||||
WorkflowDefinitionRecord = activityRecord.WorkflowDefinitionRecord
|
||||
};
|
||||
|
||||
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
|
||||
ActivityRecord = activityRecord,
|
||||
WorkflowRecord = workflow
|
||||
});
|
||||
|
||||
_workflowRepository.Create(workflow);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResumeWorkflow(AwaitingActivityRecord awaitingActivityRecord, Dictionary<string, object> tokens) {
|
||||
var lastActivity = ExecuteWorkflow(awaitingActivityRecord.WorkflowRecord.WorkflowDefinitionRecord, awaitingActivityRecord.ActivityRecord, tokens);
|
||||
|
||||
// is the workflow halted on a blocking activity ?
|
||||
if (lastActivity == null) {
|
||||
// no, delete the workflow
|
||||
_workflowRepository.Delete(awaitingActivityRecord.WorkflowRecord);
|
||||
}
|
||||
else {
|
||||
// workflow halted, save state
|
||||
awaitingActivityRecord.ActivityRecord = lastActivity;
|
||||
}
|
||||
}
|
||||
|
||||
public ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, Dictionary<string, object> tokens) {
|
||||
var firstPass = true;
|
||||
|
||||
while (true) {
|
||||
// while there is an activity to process
|
||||
var activity = _activitiesManager.GetActivityByName(activityRecord.Name);
|
||||
|
||||
if (!firstPass && activity.IsBlocking) {
|
||||
return activityRecord;
|
||||
}
|
||||
else {
|
||||
firstPass = false;
|
||||
}
|
||||
|
||||
var state = FormParametersHelper.FromJsonString(activityRecord.State);
|
||||
var activityContext = new ActivityContext {Tokens = tokens, State = state};
|
||||
var outcome = activity.Execute(activityContext);
|
||||
|
||||
if (outcome != null) {
|
||||
// look for next activity in the graph
|
||||
var transition = workflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint);
|
||||
|
||||
if (transition == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
activityRecord = transition.DestinationActivityRecord;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
.activity {
|
||||
/*.activity {
|
||||
border: 1px solid #346789;
|
||||
/*box-shadow: 2px 2px 19px #aaa;
|
||||
-o-box-shadow: 2px 2px 19px #aaa;
|
||||
-webkit-box-shadow: 2px 2px 19px #aaa;
|
||||
-moz-box-shadow: 2px 2px 19px #aaa;
|
||||
-moz-border-radius: 0.5em;*/
|
||||
-moz-border-radius: 0.5em; /
|
||||
border-radius: 0.5em;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
@@ -28,17 +28,51 @@
|
||||
-moz-box-shadow: 2px 2px 19px #444;
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
}*/
|
||||
|
||||
.activity {
|
||||
border: 1px solid #999;
|
||||
border-radius: 2px;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
/*min-width: 5em;*/
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
font-family: helvetica;
|
||||
padding: 1em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.activity:hover, .dragHover {
|
||||
box-shadow: 2px 2px 19px #aaa;
|
||||
-o-box-shadow: 2px 2px 19px #aaa;
|
||||
-webkit-box-shadow: 2px 2px 19px #aaa;
|
||||
-moz-box-shadow: 2px 2px 19px #aaa;
|
||||
/*opacity: 0.6;
|
||||
filter: alpha(opacity=60);*/
|
||||
}
|
||||
|
||||
|
||||
.blocking {
|
||||
background-image: url(images/blocking.png) ;
|
||||
background-position: top right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.start {
|
||||
background-color: #eee;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
._jsPlumb_connector { z-index:4; }
|
||||
._jsPlumb_endpoint { z-index:21;cursor:pointer; }
|
||||
|
||||
.sourceEndpoint {
|
||||
fill: red;
|
||||
}
|
||||
|
||||
.sourceEndpointLabel, .targetEndpointLabel {
|
||||
z-index: 21;
|
||||
cursor: pointer;
|
||||
@@ -65,12 +99,14 @@
|
||||
|
||||
.connection-label {
|
||||
z-index: 10;
|
||||
opacity:0.8;
|
||||
border-radius: 2px;
|
||||
filter:alpha(opacity=80);
|
||||
background-color:white;
|
||||
color:black;
|
||||
padding:0 0.5em;
|
||||
border:1px solid #346789;
|
||||
border:1px solid #999;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
|
||||
}
|
||||
|
||||
/* toolbox */
|
||||
@@ -96,3 +132,14 @@
|
||||
font-size: 1.077em;
|
||||
}
|
||||
|
||||
|
||||
#activity-overlay {
|
||||
position: absolute;
|
||||
z-index: 30;
|
||||
width: 100px;
|
||||
height: 20px;
|
||||
background-color: #EAEAEA;
|
||||
border: 1px solid #ccc;
|
||||
left: -2000px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
.diamond {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-left: 20px;
|
||||
border: 2px solid #333;
|
||||
background-color: #999;
|
||||
left: 50%;
|
||||
margin-top: 10px;
|
||||
-webkit-transform: rotate(-45deg);
|
||||
-moz-transform: rotate(-45deg);
|
||||
-ms-transform: rotate(-45deg);
|
||||
-o-transform: rotate(-45deg);
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform-origin: 0 100%;
|
||||
-moz-transform-origin: 0 100%;
|
||||
-ms-transform-origin: 0 100%;
|
||||
-o-transform-origin: 0 100%;
|
||||
transform-origin: 0 100%;
|
||||
}
|
@@ -6,6 +6,6 @@
|
||||
string blockingClass = blocking ? "blocking" : null;
|
||||
}
|
||||
|
||||
<div class="@blockingClass" style="background-color: pink">
|
||||
<div class="@blockingClass">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
||||
|
@@ -0,0 +1,12 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
}
|
||||
|
||||
<div class="@blocking">
|
||||
<div class="diamond"></div>
|
||||
@*@name.CamelFriendly()*@
|
||||
</div>
|
||||
|
@@ -13,6 +13,8 @@
|
||||
<li class="activity-toolbox-item" data-activity-name="@activity.Name">
|
||||
<h2>@activity.Name</h2>
|
||||
</li>
|
||||
|
||||
Style.Include("workflows-activity-" + activity.Name.ToLower());
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -6,6 +6,7 @@
|
||||
Layout.Title = @T("Edit Workflow");
|
||||
Style.Include("admin-workflows.css");
|
||||
Script.Require("jQueryUI");
|
||||
Style.Require("jQueryUI_Orchard");
|
||||
Script.Include("jquery.jsPlumb-1.3.16-all-min.js");
|
||||
|
||||
// var editorShape = ((IShapeFactory)New).Create(activity.Name + "_Editor");
|
||||
@@ -21,9 +22,13 @@
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<div id="activity-editor">
|
||||
<span id="activity-overlay">
|
||||
<input type="checkbox" id="initial-state" /><label for="initial-state">@T("Start")</label>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
using (Script.Head()) {
|
||||
|
||||
using (Script.Head()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var renderActivityUrl = '@Url.Action("RenderActivity", "Admin", new { area = "Orchard.Workflows" })';
|
||||
@@ -42,7 +47,7 @@ using (Script.Head()) {
|
||||
</text>
|
||||
}
|
||||
}
|
||||
else if(!Model.IsLocal) {
|
||||
else if (!Model.IsLocal) {
|
||||
<text>
|
||||
var state = @Html.Raw(Model.WorkflowDefinition.JsonData);
|
||||
sessionStorage.setItem(localId, JSON.stringify(state));
|
||||
@@ -57,7 +62,7 @@ using (Script.Head()) {
|
||||
@Html.Hidden("localId", Model.LocalId)
|
||||
@Html.Hidden("data", String.Empty)
|
||||
|
||||
using (Script.Foot()) {
|
||||
using (Script.Foot()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
$("form").submit(function () {
|
||||
@@ -68,7 +73,7 @@ using (Script.Foot()) {
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
}
|
||||
}
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit" name="submit.Save" value="@T("Save")">@T("Save")</button>
|
||||
|
@@ -59,9 +59,9 @@
|
||||
@Html.ActionLink(entry.Name, "Edit", new { id = entry.WokflowDefinitionId })
|
||||
</td>
|
||||
<td>
|
||||
@*@Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.QueryId, returnurl = HttpContext.Current.Request.RawUrl })*@ |
|
||||
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.WokflowDefinitionId}) |
|
||||
@*@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.QueryId }, new { itemprop = "RemoveUrl UnsafeUrl" })*@
|
||||
@* TODO: As WD is not a Content Item, an EditProperties action is necessary @Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.WokflowDefinitionId, returnurl = HttpContext.Current.Request.RawUrl }) |*@
|
||||
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.WokflowDefinitionId }) |
|
||||
@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.WokflowDefinitionId }, new { itemprop = "RemoveUrl UnsafeUrl" })
|
||||
</td>
|
||||
</tr>
|
||||
index++;
|
||||
|
Reference in New Issue
Block a user