Initial editor persistence

This commit is contained in:
Sipke Schoorstra 2018-12-03 22:02:40 +01:00
parent 9819534eee
commit 66cd7c04e6
61 changed files with 535 additions and 195 deletions

View file

@ -2,6 +2,10 @@
{
public class WorkflowExpression<T>
{
public WorkflowExpression()
{
}
public WorkflowExpression(string syntax, string expression)
{
Syntax = syntax;
@ -9,7 +13,7 @@
}
public string Syntax { get; set; }
public string Expression { get; }
public string Expression { get; set; }
public override string ToString() => Expression;
}

View file

@ -1,16 +1,27 @@
using System;
using Flowsharp.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace Flowsharp.Extensions
{
public static class ActivityDescriptorExtensions
{
public static IActivity InstantiateActivity(this ActivityDescriptor descriptor, string json = null)
public static IActivity InstantiateActivity(this ActivityDescriptor descriptor, string json)
{
var activity = json == null
var token = json != null ? JToken.Parse(json) : default;
return InstantiateActivity(descriptor, token);
}
public static IActivity InstantiateActivity(this ActivityDescriptor descriptor, JToken token = default)
{
var activity = token == null
? Activator.CreateInstance(descriptor.ActivityType)
: JsonConvert.DeserializeObject(json, descriptor.ActivityType);
: token.ToObject(descriptor.ActivityType, JsonSerializer.Create(new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}));
return (IActivity) activity;
}

View file

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
namespace Flowsharp.Extensions
{
public static class ExpandObjectExtensions
{
public static T Get<T>(this ExpandoObject expandoObject, string key, Func<T> defaultValue = null)
{
var dictionary = (IDictionary<string, object>) expandoObject;
return dictionary.ContainsKey(key) ? (T)dictionary[key] : defaultValue != null ? defaultValue() : default;
}
}
}

View file

@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>
</Project>

View file

@ -4,7 +4,7 @@ namespace Flowsharp
{
public interface IActivity
{
int Id { get; set; }
string Id { get; set; }
string Name { get; }
ActivityMetadata Metadata { get; set; }
}

View file

@ -7,7 +7,7 @@ namespace Flowsharp
{
public interface IActivityLibrary
{
Task<IEnumerable<Models.ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
Task<Models.ActivityDescriptor> GetActivityByNameAsync(string name, CancellationToken cancellationToken);
Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
Task<ActivityDescriptor> GetActivityByNameAsync(string name, CancellationToken cancellationToken);
}
}

View file

@ -7,6 +7,6 @@ namespace Flowsharp
{
public interface IActivityProvider
{
Task<IEnumerable<Models.ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
}
}

View file

@ -1,8 +1,8 @@
namespace Flowsharp.Models
{
public abstract class Activity : Flowsharp.IActivity
public abstract class Activity : IActivity
{
public int Id { get; set; }
public string Id { get; set; }
public string Name => GetType().Name;
public ActivityMetadata Metadata { get; set; } = new ActivityMetadata();
}

View file

@ -2,19 +2,19 @@
{
public class ActivityExecutionContext
{
public ActivityExecutionContext(Flowsharp.IActivity activity, ActivityDescriptor descriptor)
public ActivityExecutionContext(IActivity activity, ActivityDescriptor descriptor)
{
Activity = activity;
Descriptor = descriptor;
}
public Flowsharp.IActivity Activity { get; }
public IActivity Activity { get; }
public ActivityDescriptor Descriptor { get; }
}
public class ActivityExecutionContext<T> : ActivityExecutionContext where T : Flowsharp.IActivity
public class ActivityExecutionContext<T> : ActivityExecutionContext where T : IActivity
{
public ActivityExecutionContext(Flowsharp.IActivity activity, ActivityDescriptor descriptor) : base(activity, descriptor)
public ActivityExecutionContext(IActivity activity, ActivityDescriptor descriptor) : base(activity, descriptor)
{
}

View file

@ -1,9 +1,9 @@
using System.Dynamic;
using Newtonsoft.Json.Linq;
namespace Flowsharp.Models
{
public class ActivityMetadata
{
public ExpandoObject CustomFields { get; set; } = new ExpandoObject();
public JObject CustomFields { get; set; } = new JObject();
}
}

View file

@ -6,11 +6,11 @@ namespace Flowsharp.Models
{
}
public Connection(Flowsharp.IActivity source, Flowsharp.IActivity target) : this(new SourceEndpoint(source), new TargetEndpoint(target))
public Connection(IActivity source, IActivity target) : this(new SourceEndpoint(source), new TargetEndpoint(target))
{
}
public Connection(Flowsharp.IActivity source, string sourceEndpointName, Flowsharp.IActivity target) : this(new SourceEndpoint(source, sourceEndpointName), new TargetEndpoint(target))
public Connection(IActivity source, string sourceEndpointName, IActivity target) : this(new SourceEndpoint(source, sourceEndpointName), new TargetEndpoint(target))
{
}

View file

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace Flowsharp.Models
{
public class CustomFieldDictionary : Dictionary<string, JToken>
{
}
}

View file

@ -6,11 +6,11 @@ namespace Flowsharp.Models
{
}
protected Endpoint(Flowsharp.IActivity activity)
protected Endpoint(IActivity activity)
{
Activity = activity;
}
public Flowsharp.IActivity Activity { get; set; }
public IActivity Activity { get; set; }
}
}

View file

@ -6,7 +6,7 @@ namespace Flowsharp.Models
{
}
public SourceEndpoint(Flowsharp.IActivity activity, string name = null) : base(activity)
public SourceEndpoint(IActivity activity, string name = null) : base(activity)
{
Name = name;
}

View file

@ -6,7 +6,7 @@ namespace Flowsharp.Models
{
}
public TargetEndpoint(Flowsharp.IActivity activity) : base(activity)
public TargetEndpoint(IActivity activity) : base(activity)
{
}
}

View file

@ -1,32 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace Flowsharp.Models
{
public class Workflow
{
public Workflow(IEnumerable<Flowsharp.IActivity> activities, IEnumerable<Connection> connections) : this()
public Workflow(IEnumerable<IActivity> activities, IEnumerable<Connection> connections) : this()
{
Activities = activities.ToList();
Connections = connections.ToList();
}
public Workflow()
{
CurrentScope = new WorkflowExecutionScope();
Scopes = new Stack<WorkflowExecutionScope>(new[]{ CurrentScope });
Scopes = new Stack<WorkflowExecutionScope>(new[] { CurrentScope });
Arguments = new Variables();
BlockingActivities = new List<Flowsharp.IActivity>();
BlockingActivities = new List<IActivity>();
Metadata = new WorkflowMetadata();
}
[JsonConverter(typeof(StringEnumConverter))]
public WorkflowStatus Status { get; set; }
public IList<Flowsharp.IActivity> Activities { get; set; } = new List<Flowsharp.IActivity>();
public IList<IActivity> Activities { get; set; } = new List<IActivity>();
public IList<Connection> Connections { get; set; } = new List<Connection>();
public Stack<WorkflowExecutionScope> Scopes { get; set; }
public WorkflowExecutionScope CurrentScope { get; set; }
public Variables Arguments { get; set; }
public IList<Flowsharp.IActivity> BlockingActivities { get; set; }
public IList<IActivity> BlockingActivities { get; set; }
public WorkflowMetadata Metadata { get; set; }
public WorkflowFault Fault { get; set; }
}

View file

@ -7,20 +7,20 @@ namespace Flowsharp.Models
public class WorkflowExecutionContext
{
private readonly IDictionary<string, ActivityDescriptor> activityDescriptors;
private readonly Stack<Flowsharp.IActivity> scheduledActivities;
private readonly Stack<IActivity> scheduledActivities;
public WorkflowExecutionContext(Workflow workflow, IDictionary<string, ActivityDescriptor> activityDescriptors)
{
this.activityDescriptors = activityDescriptors;
Workflow = workflow;
IsFirstPass = true;
scheduledActivities = new Stack<Flowsharp.IActivity>();
scheduledActivities = new Stack<IActivity>();
}
public Workflow Workflow { get; }
public bool HasScheduledActivities => scheduledActivities.Any();
public bool IsFirstPass { get; set; }
public Flowsharp.IActivity CurrentActivity { get; private set; }
public IActivity CurrentActivity { get; private set; }
public WorkflowExecutionScope CurrentScope
{
@ -28,7 +28,7 @@ namespace Flowsharp.Models
set => Workflow.CurrentScope = value;
}
public ActivityExecutionContext CreateActivityExecutionContext(Flowsharp.IActivity activity) =>
public ActivityExecutionContext CreateActivityExecutionContext(IActivity activity) =>
new ActivityExecutionContext(activity, activityDescriptors[activity.Name]);
public void BeginScope()
@ -42,12 +42,12 @@ namespace Flowsharp.Models
CurrentScope = Workflow.Scopes.Peek();
}
public void ScheduleActivity(Flowsharp.IActivity activity)
public void ScheduleActivity(IActivity activity)
{
scheduledActivities.Push(activity);
}
public Flowsharp.IActivity PopScheduledActivity()
public IActivity PopScheduledActivity()
{
CurrentActivity = scheduledActivities.Pop();
return CurrentActivity;
@ -58,9 +58,9 @@ namespace Flowsharp.Models
CurrentScope.LastResult = value;
}
public void Fault(Exception exception, Flowsharp.IActivity activity) => Fault(exception.Message, activity);
public void Fault(Exception exception, IActivity activity) => Fault(exception.Message, activity);
public void Fault(string errorMessage, Flowsharp.IActivity activity)
public void Fault(string errorMessage, IActivity activity)
{
Workflow.Fault = new WorkflowFault
{

View file

@ -2,7 +2,7 @@
{
public class WorkflowFault
{
public Flowsharp.IActivity FaultedActivity { get; set; }
public IActivity FaultedActivity { get; set; }
public string Message { get; set; }
}
}

View file

@ -1,4 +1,4 @@
using System.Dynamic;
using Newtonsoft.Json.Linq;
namespace Flowsharp.Models
{
@ -8,6 +8,6 @@ namespace Flowsharp.Models
public string ParentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ExpandoObject CustomFields { get; set; }
public JObject CustomFields { get; set; }
}
}

View file

@ -0,0 +1,23 @@
using System.IO;
using System.Text;
namespace Flowsharp.Serialization.Extensions
{
public static class StringExtensions
{
public static Stream ToStream(this string text)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return stream;
}
public static byte[] ToBytes(this string text)
{
return Encoding.UTF8.GetBytes(text);
}
}
}

View file

@ -5,7 +5,7 @@ namespace Flowsharp.Serialization.Tokenizers
{
public class WorkflowTokenizationContext
{
public IDictionary<IActivity, int> ActivityIdLookup { get; set; }
public IDictionary<int, IActivity> ActivityLookup { get; set; }
public IDictionary<IActivity, string> ActivityIdLookup { get; set; }
public IDictionary<string, IActivity> ActivityLookup { get; set; }
}
}

View file

@ -15,7 +15,7 @@ namespace Flowsharp
this.providers = providers;
}
public async Task<IEnumerable<Models.ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken)
public async Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken)
{
var tasks = providers.Select(x => x.GetActivitiesAsync(cancellationToken));
var descriptorsList = await Task.WhenAll(tasks);
@ -24,7 +24,7 @@ namespace Flowsharp
return descriptors;
}
public async Task<Models.ActivityDescriptor> GetActivityByNameAsync(string name, CancellationToken cancellationToken)
public async Task<ActivityDescriptor> GetActivityByNameAsync(string name, CancellationToken cancellationToken)
{
var descriptors = await GetActivitiesAsync(cancellationToken);
return descriptors.SingleOrDefault(x => x.Name == name);

View file

@ -14,7 +14,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="YamlDotNet.NetStandard" Version="4.0.0" />
</ItemGroup>

View file

@ -15,7 +15,7 @@ namespace Flowsharp.Serialization.Tokenizers
protected override IActivity Detokenize(WorkflowTokenizationContext context, JToken value)
{
var obj = (JObject) value;
var activityId = obj["activityId"].Value<int>();
var activityId = obj["activityId"].Value<string>();
return context.ActivityLookup[activityId];
}
}

View file

@ -26,7 +26,7 @@ namespace Flowsharp.Serialization.Tokenizers
public Task<JToken> TokenizeAsync(Workflow value, CancellationToken cancellationToken)
{
var workflow = value;
var activityTuples = workflow.Activities.Select((x, i) => new {Activity = x, Id = i + 1}).ToList();
var activityTuples = workflow.Activities.Select(x => new {Activity = x, Id = Guid.NewGuid().ToString()}).ToList();
var context = new WorkflowTokenizationContext
{
ActivityIdLookup = activityTuples.ToDictionary(x => x.Activity, x => x.Id),
@ -160,7 +160,7 @@ namespace Flowsharp.Serialization.Tokenizers
var activity = item.Key;
var activityModel = JObject.FromObject(activity, new JsonSerializer { ContractResolver = new CamelCasePropertyNamesContractResolver()});
activityModel.AddFirst(new JProperty("name", activity.Name));
activityModel["name"] = activity.Name;
activityModels.Add(activityModel);
}
@ -196,27 +196,27 @@ namespace Flowsharp.Serialization.Tokenizers
return scopeLookup;
}
private IEnumerable<IActivity> DeserializeHaltedActivities(JToken token, IDictionary<int, IActivity> activityDictionary)
private IEnumerable<IActivity> DeserializeHaltedActivities(JToken token, IDictionary<string, IActivity> activityDictionary)
{
var haltedActivityModels = (JArray) token["haltedActivities"] ?? new JArray();
foreach (var haltedActivityModel in haltedActivityModels)
{
var activityId = haltedActivityModel.Value<int>();
var activityId = haltedActivityModel.Value<string>();
var activity = activityDictionary[activityId];
yield return activity;
}
}
private IEnumerable<Connection> DeserializeConnections(JToken token, IDictionary<int, IActivity> activityDictionary)
private IEnumerable<Connection> DeserializeConnections(JToken token, IDictionary<string, IActivity> activityDictionary)
{
var connectionModels = (JArray) token["connections"] ?? new JArray();
foreach (var connectionModel in connectionModels)
{
var sourceActivityId = connectionModel["source"]["activityId"].Value<int>();
var sourceActivityId = connectionModel["source"]["activityId"].Value<string>();
var sourceEndpointName = connectionModel["source"]["endpoint"].Value<string>();
var targetActivityId = connectionModel["target"]["activityId"].Value<int>();
var targetActivityId = connectionModel["target"]["activityId"].Value<string>();
var sourceActivity = activityDictionary[sourceActivityId];
var targetActivity = activityDictionary[targetActivityId];
@ -224,10 +224,10 @@ namespace Flowsharp.Serialization.Tokenizers
}
}
private async Task<IDictionary<int, IActivity>> DeserializeActivitiesAsync(JToken token, CancellationToken cancellationToken)
private async Task<IDictionary<string, IActivity>> DeserializeActivitiesAsync(JToken token, CancellationToken cancellationToken)
{
var activityModels = (JArray)token["activities"] ?? new JArray();
var dictionary = new Dictionary<int, IActivity>();
var dictionary = new Dictionary<string, IActivity>();
foreach (var activityModel in activityModels)
{
@ -241,7 +241,7 @@ namespace Flowsharp.Serialization.Tokenizers
return dictionary;
}
private Task<Models.ActivityDescriptor> GetActivityDescriptorAsync(string activityName, CancellationToken cancellationToken)
private Task<ActivityDescriptor> GetActivityDescriptorAsync(string activityName, CancellationToken cancellationToken)
{
return activityLibrary.GetActivityByNameAsync(activityName, cancellationToken);
}

View file

@ -4,9 +4,10 @@
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="OrchardCore.Application.Mvc.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Resources" Version="1.0.0-beta3-69736" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
<PackageReference Include="OrchardCore.Application.Mvc.Targets" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Resources" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Settings" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -1,7 +1,10 @@
using Flowsharp.Web.OrchardCoreLite.Extensions;
using System.Linq;
using System.Reflection;
using Flowsharp.Web.OrchardCoreLite.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using OrchardCore.Modules.Manifest;
namespace Flowsharp.Web.Dashboard
{
@ -21,7 +24,7 @@ namespace Flowsharp.Web.Dashboard
app.UseDeveloperExceptionPage();
}
app.UseOrchardCore();;
app.UseOrchardCore();
}
}
}

View file

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -10,7 +10,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -2,5 +2,5 @@
<header>
<h4><i class="fa fa-code-fork"></i>Write Line</h4>
<i>&quot;Hello World&quot;</i>
<i>&quot;@Model.Activity.TextExpression&quot;</i>
</header>

View file

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,44 @@
{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/bootstrap": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-4.1.2.tgz",
"integrity": "sha512-BiC/IxqyLS68UFp71t2Px80ql/FpaUKSy469aa6HTDFRkv/FvdzRZjF6ifDql/VKSLyin5AZa/yARLetMFM4rQ==",
"dev": true,
"requires": {
"@types/jquery": "*",
"popper.js": "^1.14.1"
}
},
"@types/jquery": {
"version": "3.3.22",
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.3.22.tgz",
"integrity": "sha512-a4JDcIhJhHYnoWCkG3xT2CZxXZeA92JeREESorg0DMQ3ZsjuKF48h7XK4l5Gl2GRa/ItGRpKMT0pyK88yRgqXQ==",
"dev": true,
"requires": {
"@types/sizzle": "*"
}
},
"@types/node": {
"version": "10.12.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.11.tgz",
"integrity": "sha512-3iIOhNiPGTdcUNVCv9e5G7GotfvJJe2pc9w2UgDXlUwnxSZ3RgcUocIU+xYm+rTU54jIKih998QE4dMOyMN1NQ==",
"dev": true
},
"@types/sizzle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
"integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
"dev": true
},
"popper.js": {
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.14.6.tgz",
"integrity": "sha512-AGwHGQBKumlk/MDfrSOf0JHhJCImdDMcGNoqKmKkU+68GFazv3CQ6q9r7Ja1sKDZmYWTckY/uLyEznheTDycnA==",
"dev": true
}
}
}

View file

@ -0,0 +1,10 @@
{
"private": true,
"version": "1.0.0",
"devDependencies": {
"@types/jquery": "^3.3.22",
"@types/node": "^10.12.2",
"@types/bootstrap": "^4.1.2"
},
"dependencies": {}
}

View file

@ -0,0 +1,86 @@
///<reference path="../../../Flowsharp.Web.ViewComponents/Assets/scripts/flowsharp/workflow-designer.ts"/>
namespace Flowsharp {
const win: any = window;
export class WorkflowEditor {
private workflowDesigner: WorkflowDesigner;
private containerElement: JQuery<HTMLElement>;
private workflowId: string;
constructor(container: HTMLElement,) {
this.containerElement = $(container);
const saveButton = this.containerElement.find('.save-workflow-button');
const downloadButton = this.containerElement.find('.download-workflow-button');
this.workflowDesigner = (<any>window).Flowsharp.workflowDesigner;
this.workflowId = this.containerElement.data('workflow-id');
saveButton.on('click', this.onSaveButtonClick);
downloadButton.on('click', this.onDownloadButtonClick);
}
private onSaveButtonClick = (e: JQuery.Event) => {
e.preventDefault();
const workflow = this.workflowDesigner.getWorkflow();
const workflowJson = JSON.stringify(workflow);
$.ajax({
url: `/workflows/${this.workflowId}`,
type: 'PUT',
data: workflowJson,
contentType: 'application/json',
dataType: 'json'
}).done(this.displayNotification);
};
private onDownloadButtonClick = (e: JQuery.Event) => {
e.preventDefault();
const workflowJson = this.serializeWorkflow(false);
$.ajax({
url: `/workflows/${this.workflowId}/download`,
method: 'POST',
data: workflowJson,
contentType: 'application/json',
xhrFields: {
responseType: 'blob'
},
success: (data) => {
this.downloadData(data);
}
});
};
private downloadJson = () => {
const workflowJson = this.serializeWorkflow(true);
const data = new Blob([workflowJson], { type: 'text/yaml' });
this.downloadData(data);
};
private downloadData = (data: any) => {
const a = document.createElement('a');
const url = window.URL.createObjectURL(data);
a.href = url;
a.download = this.workflowId;
a.click();
window.URL.revokeObjectURL(url);
};
private serializeWorkflow = (pretty: boolean): string => {
const workflow = this.workflowDesigner.getWorkflow();
return JSON.stringify(workflow, null, pretty ? 2 : null);
};
private displayNotification = () => {
alert("Saved!");
};
}
$(() => {
$('.workflow-editor-container').each((i, e) => {
const editor = new WorkflowEditor(e);
});
});
}

View file

@ -1,34 +1,64 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Flowsharp.Models;
using Flowsharp.Persistence;
using Flowsharp.Persistence.Specifications;
using Flowsharp.Serialization;
using Flowsharp.Serialization.Extensions;
using Flowsharp.Serialization.Formatters;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using OrchardCore.Modules;
namespace Flowsharp.Web.Management.Controllers
{
[Route("[controller]")]
[IgnoreAntiforgeryToken]
public class WorkflowsController : Controller
{
private readonly IWorkflowStore workflowStore;
private readonly IWorkflowSerializer workflowSerializer;
private readonly ITokenFormatter tokenFormatter;
public WorkflowsController(IWorkflowStore workflowStore)
public WorkflowsController(
IWorkflowStore workflowStore,
IWorkflowSerializer workflowSerializer,
ITokenFormatter tokenFormatter)
{
this.workflowStore = workflowStore;
this.workflowSerializer = workflowSerializer;
this.tokenFormatter = tokenFormatter;
}
public async Task<IActionResult> Index(CancellationToken cancellationToken)
{
var workflowDefinitions = await workflowStore.GetManyAsync(new AllWorkflows(), cancellationToken);
return View(workflowDefinitions.ToList());
var workflows = await workflowStore.GetManyAsync(new AllWorkflows(), cancellationToken);
return View(workflows.ToList());
}
[HttpGet("edit/{id}")]
[HttpGet("{id}")]
public async Task<IActionResult> Edit(string id, CancellationToken cancellationToken)
{
var workflowDefinition = await workflowStore.GetAsync(id, cancellationToken);
return View(workflowDefinition);
var workflow = await workflowStore.GetAsync(id, cancellationToken);
return View(workflow);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(string id, [FromBody]JToken workflowData, CancellationToken cancellationToken)
{
var workflow = await workflowSerializer.DeserializeAsync(workflowData, cancellationToken);
await workflowStore.UpdateAsync(workflow, cancellationToken);
return Ok(workflow);
}
[HttpPost("{id}/download")]
public IActionResult Download(string id, [FromBody]JToken workflowData, CancellationToken cancellationToken)
{
var data = tokenFormatter.ToString(workflowData);
var bytes = data.ToStream();
return File(bytes, "application/x-yaml", id);
}
}
}

View file

@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<Content Include="Assets\scripts\workflow-editor.ts" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Views\Workflows\Edit.cshtml" />
<Content Include="Views\Workflows\Index.cshtml" />
@ -12,10 +13,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.FileStorage.FileSystem" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Mvc.Core" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.FileStorage.FileSystem" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Mvc.Core" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -1,12 +1,6 @@
@model Workflow
<script
asp-name="test"
asp-src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.20.13/system.js"
at="Head">
</script>
<div class="full-container">
<div class="full-container workflow-editor-container" data-workflow-id="@Model.Metadata.Id">
<div class="peers fxw-nw pos-r">
<div class="peer bdR" id="chat-sidebar">
<div class="layers h-100">
@ -35,18 +29,15 @@
</a>
</div>
<div class="peer">
<h5 class="c-grey-900 mB-5">Sample Workflow</h5><i class="fsz-sm lh-1">This workflow is a sample workflow demonstrating core workflow functionality</i>
<h5 class="c-grey-900 mB-5">@Model.Metadata.Name</h5><i class="fsz-sm lh-1">@Model.Metadata.Description</i>
</div>
</div>
<div class="peers">
<a href="" class="peer td-n c-grey-900 cH-blue-500 fsz-md mR-30" title="">
<i class="ti-video-camera"></i>
<a href="" class="save-workflow-button peer td-n c-grey-900 cH-blue-500 fsz-md mR-30" title="@T["Save Workflow"]">
<i class="ti-save"></i>
</a>
<a href="" class="peer td-n c-grey-900 cH-blue-500 fsz-md mR-30" title="">
<i class="ti-headphone"></i>
</a>
<a href="" class="peer td-n c-grey-900 cH-blue-500 fsz-md" title="">
<i class="ti-more"></i>
<a href="" class="download-workflow-button peer td-n c-grey-900 cH-blue-500 fsz-md" title="@T["Download"]">
<i class="ti-download"></i>
</a>
</div>
</div>
@ -62,17 +53,9 @@
<div class="ps__thumb-y" tabindex="0" style="top: 0px; height: 0px;"></div>
</div>
</div>
<div class="layer w-100">
<div class="p-20 bdT bgc-white">
<div class="pos-r">
<input type="text" class="form-control bdrs-10em m-0" placeholder="Say something...">
<button type="button" class="btn btn-primary bdrs-50p w-2r p-0 h-2r pos-a r-1 t-1">
<i class="fa fa-paper-plane-o"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script asp-name="flowsharp-workflow-editor-script" asp-src="~/Flowsharp.Web.Management/workflow-editor.js" depends-on="jQuery" at="Foot"></script>

View file

@ -0,0 +1,8 @@
[
{
"inputs": [
"assets/scripts/workflow-editor.ts"
],
"output": "wwwroot/workflow-editor.js"
}
]

View file

@ -5,12 +5,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Infrastructure" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Settings" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Infrastructure" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Settings" Version="1.0.0-beta3-69807" />
</ItemGroup>
</Project>

View file

@ -23,7 +23,7 @@ namespace Flowsharp.Web.OrchardCoreLite
services.AddScoped<IShellDescriptorFeaturesManager, ShellDescriptorFeaturesManager>();
services.AddScoped<IShellStateManager, NullShellStateManager>();
services.AddScoped<IThemeSelector, SettingsThemeSelector>();
services.AddScoped<ISiteService, NullSiteService>();
services.AddSingleton<ISiteService, NullSiteService>();
services.AddResourceManagement();

View file

@ -5,8 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.FileStorage.FileSystem" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.FileStorage.FileSystem" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>

View file

@ -1,10 +1,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Flowsharp.Models;
using Flowsharp.Persistence;
using Flowsharp.Serialization;
using Flowsharp.Serialization.Extensions;
using Flowsharp.Serialization.Formatters;
using Flowsharp.Web.Persistence.FileSystem.Extensions;
using OrchardCore.FileStorage;
@ -43,15 +45,22 @@ namespace Flowsharp.Web.Persistence.FileSystem.Services
throw new System.NotImplementedException();
}
public Task UpdateAsync(Workflow value, CancellationToken cancellationToken)
public async Task UpdateAsync(Workflow value, CancellationToken cancellationToken)
{
throw new System.NotImplementedException();
await fileStore.TryCreateDirectoryAsync("workflows");
var data = await workflowSerializer.SerializeAsync(value, cancellationToken);
var path = fileStore.Combine("workflows", value.Metadata.Id);
using (var stream = data.ToStream())
{
await fileStore.CreateFileFromStream(path, stream, true);
}
}
private async Task<IEnumerable<Workflow>> ListAsync(CancellationToken cancellationToken)
{
await fileStore.TryCreateDirectoryAsync("definitions");
var files = await fileStore.GetDirectoryContentAsync("definitions");
await fileStore.TryCreateDirectoryAsync("workflows");
var files = await fileStore.GetDirectoryContentAsync("workflows");
var loadTasks = files.Select(x => LoadWorkflowDefinitionAsync(x, cancellationToken));
return await Task.WhenAll(loadTasks);
}

View file

@ -8,7 +8,6 @@
},
"dependencies": {
"decode-html": "^2.0.0",
"jsplumb": "^2.8.3",
"requirejs": "^2.3.6"
"jsplumb": "^2.8.3"
}
}

View file

@ -5,7 +5,6 @@
namespace Flowsharp {
export class ActivityEditor {
private baseUrl: string;
private containerElement: JQuery<HTMLElement>;
private titleElement: JQuery<HTMLElement>;
private contentElement: JQuery<HTMLElement>;
@ -18,7 +17,6 @@ namespace Flowsharp {
this.titleElement = this.containerElement.find('.modal-title');
this.contentElement = this.containerElement.find('.modal-body');
this.formElement = <JQuery<HTMLFormElement>>this.containerElement.find('form');
this.baseUrl = this.containerElement.data('base-url');
this.formElement.on('submit', this.onFormSubmit);
}
@ -29,11 +27,13 @@ namespace Flowsharp {
public display = (activityName: string, activity: IActivity): JQuery.Promise<any> => {
this.activityName = activityName;
this.deferred = jQuery.Deferred<any>();
const action = activity.id == null ? 'create' : 'edit';
const activityJson: string = JSON.stringify(activity);
$.ajax({
type: 'POST',
url: `/activity/edit/${activityName}`,
data: activity,
url: `/activity/${action}/${activityName}`,
data: activityJson,
contentType: 'application/json',
dataType: 'html'
}).done(this.displayActivity);

View file

@ -0,0 +1,5 @@
namespace Flowsharp {
export interface IActivityMetadata {
customFields: any;
}
}

View file

@ -1,7 +1,10 @@
///<reference path="activity-metadata.ts"/>
///<reference path="endpoint.ts"/>
namespace Flowsharp {
export interface IActivity {
id: string;
endpoints: IEndpoint[];
metadata: IActivityMetadata;
}
}

View file

@ -1,5 +1,5 @@
namespace Flowsharp {
export interface ITransition {
export interface IConnection {
sourceActivityId: string;
sourceOutcomeName: string;
destinationActivityId: null;

View file

@ -16,15 +16,16 @@ namespace Flowsharp {
}
static getSourceEndpointOptions(activityId: any, endpointName: any): any {
const paintColor = '#7ab02c';
const fill = '#ffeb3b';
const stroke = '#ffeb3b';
return {
endpoint: 'Dot',
anchor: 'Continuous',
paintStyle: {
stroke: paintColor,
fill: paintColor,
stroke: stroke,
fill: fill,
radius: 7,
strokeWidth: 1
strokeWidth: 5
},
isSource: true,
connector: ['Flowchart', {stub: [40, 60], gap: 0, cornerRadius: 5, alwaysRespectStubs: true}],
@ -36,12 +37,12 @@ namespace Flowsharp {
outlineWidth: 2
},
hoverPaintStyle: {
fill: '#216477',
stroke: '#216477'
fill: stroke,
stroke: fill
},
connectorHoverStyle: {
strokeWidth: 3,
stroke: '#216477',
stroke: stroke,
outlineWidth: 5,
outlineStroke: 'white'
},

View file

@ -3,11 +3,14 @@
///<reference path='./workflow-designer-config.ts' />
///<reference path='./activity-editor.ts' />
///<reference path="activity-descriptor.ts"/>
///<reference path="workflow.ts"/>
namespace Flowsharp {
export class WorkflowDesigner {
private plumber: any;
private activityEditor: ActivityEditor;
private dragStart: { left: number; top: number };
private hasDragged: boolean;
constructor(private readonly container: HTMLElement, private readonly activityEditorContainer: HTMLElement) {
this.plumber = WorkflowDesignerConfig.createJsPlumbInstance(container);
@ -15,36 +18,66 @@ namespace Flowsharp {
this.initializeDropTarget();
this.activityEditor = new ActivityEditor(activityEditorContainer);
}
private static onDragOver = (e: JQuery.Event) => {
e.preventDefault();
public getWorkflow = (): IWorkflow => {
const containerElement = $(this.container);
const workflowMetadata: IWorkflowMetadata = containerElement.data('workflow-metadata');
const workflowStatus: WorkflowStatus = containerElement.data('workflow-status');
const activityElements = containerElement.find('.activity');
const activities: IActivity[] = [];
for (let index = 0; index < activityElements.length; index++) {
const activityElement = $(activityElements[index]);
const activity: IActivity = activityElement.data('activity-model');
activities.push(activity);
}
return {
metadata: workflowMetadata,
activities: activities,
connections: [],
status: workflowStatus
};
};
private editActivity = (activityElement: JQuery<HTMLElement>) => {
const displayText = activityElement.attr('title');
const activityName = activityElement.data('activity-name');
const activity:IActivity = activityElement.data('activity-model');
const activity: IActivity = activityElement.data('activity-model');
this.activityEditor.title = `Edit ${displayText}`;
this.activityEditor.show();
this.activityEditor.display(activityName, activity).done(this.updateActivity);
this.activityEditor
.display(activityName, activity)
.done((updatedHtml: string) => this.updateActivityElement(activityElement, updatedHtml));
};
private addActivity = (activityHtml: string, x: number, y: number) => {
private addActivity = (activityHtml: string) => {
const activityElement = $(activityHtml);
activityElement.css({ x: x, y: y });
this.container.appendChild(activityElement[0]);
this.initializeElement(activityElement);
};
private updateActivity = (activityHtml: string) => {
const activityElement = $(activityHtml);
//this.initializeElement(activityElement);
private updateActivityElement = (activityElement: JQuery<HTMLElement>, updatedHtml: string) => {
this.plumber.deleteConnectionsForElement(activityElement);
this.plumber.removeAllEndpoints(activityElement);
activityElement.remove();
const updatedActivityElement = $(updatedHtml);
this.container.appendChild(updatedActivityElement[0]);
this.initializeElement(updatedActivityElement);
};
private updateActivityModel = (activityElement: JQuery<HTMLElement>, update: (activity: IActivity) => any) => {
const model: IActivity = activityElement.data('activity-model');
update(model);
activityElement.data('activity-model', model);
};
private initializeNodes = () => {
this.plumber.batch(() => {
let activityElements = $(this.container).find('.activity');
const activityElements = $(this.container).find('.activity');
for (let index = 0; index < activityElements.length; index++) {
const activityElement = $(activityElements[index]);
@ -60,27 +93,40 @@ namespace Flowsharp {
private initializeElement = (activityElement: JQuery<HTMLElement>) => {
const activityId: string = activityElement.data('activity-id');
const activityEndpoints: any[] = activityElement.data('activity-endpoints');
this.plumber.draggable(activityElement, {
grid: [10, 10],
containment: true,
// start: args => {
// //this.dragStart = {left: args.e.screenX, top: args.e.screenY};
// },
// stop: args => {
// //this.hasDragged = this.dragStart.left !== args.e.screenX || this.dragStart.top !== args.e.screenY;
// }
start: (args: any) => {
this.dragStart = {left: args.e.screenX, top: args.e.screenY};
},
stop: (args: any) => {
this.hasDragged = this.dragStart.left !== args.e.screenX || this.dragStart.top !== args.e.screenY;
if (!this.hasDragged) {
return;
}
this.updateActivityModel(activityElement, activity => {
const designer = activity.metadata.customFields.designer || {x: 0, y: 0};
jQuery.extend(designer, {
x: args.pos[0],
y: args.pos[1]
});
activity.metadata.customFields.designer = designer;
});
}
});
this.plumber.makeTarget(activityElement, {
dropOptions: {hoverClass: 'hover'},
anchor: 'Continuous',
endpoint: ['Blank', {radius: 8}]
});
this.addSourceEndpoints(activityElement[0], activityId, activityEndpoints);
activityElement.on('dblclick', this.onDoubleClick);
this.plumber.revalidate(activityElement[0]);
};
private addSourceEndpoints = (activityElement: HTMLElement, activityId: string, endpoints: any[]) => {
@ -101,6 +147,10 @@ namespace Flowsharp {
$(this.container).on('drop', this.onDrop)
};
private static onDragOver = (e: JQuery.Event) => {
e.preventDefault();
};
private onDrop = (e: JQuery.Event) => {
e.preventDefault();
@ -109,10 +159,19 @@ namespace Flowsharp {
const activityName = activityDescriptor.name;
const x = dragEvent.offsetX;
const y = dragEvent.offsetY;
const activity: IActivity = {
id: null,
endpoints: [],
metadata: {
customFields: {
designer: {x: x, y: y}
}
}
};
this.activityEditor.title = `Add ${activityDescriptor.displayText}`;
this.activityEditor.show();
this.activityEditor.display(activityName, null).done((data: string) => this.addActivity(data, x, y));
this.activityEditor.display(activityName, activity).done((data: string) => this.addActivity(data));
};
private onDoubleClick = (e: JQuery.Event) => {
@ -123,11 +182,15 @@ namespace Flowsharp {
};
}
$(function () {
$(() => {
$('.workflow-designer-container').each((i, e) => {
const canvasContainer = $(e).find('.workflow-canvas')[0];
const activityEditorContainer = $(e).find('.activity-editor-modal')[0];
const editor = new WorkflowDesigner(canvasContainer, activityEditorContainer);
const workflowDesigner = new WorkflowDesigner(canvasContainer, activityEditorContainer);
const win = <any>window;
win.Flowsharp = win.Flowsharp || {};
win.Flowsharp.workflowDesigner = workflowDesigner;
});
});
}

View file

@ -0,0 +1,9 @@
namespace Flowsharp {
export interface IWorkflowMetadata {
id: string;
parentId: string;
name: string;
description: string;
customFields: any;
}
}

View file

@ -0,0 +1,12 @@
namespace Flowsharp {
export enum WorkflowStatus {
Idle,
Starting,
Resuming,
Executing,
Halted,
Finished,
Faulted,
Aborted
}
}

View file

@ -0,0 +1,13 @@
///<reference path="activity.ts"/>
///<reference path="connection.ts"/>
///<reference path="workflow-metadata.ts"/>
///<reference path="workflow-status.ts"/>
namespace Flowsharp {
export interface IWorkflow {
metadata: IWorkflowMetadata;
activities: IActivity[];
connections: IConnection[];
status: WorkflowStatus;
}
}

View file

@ -25,8 +25,8 @@ namespace Flowsharp.Web.ViewComponents.Controllers
private readonly IActivityShapeFactory activityShapeFactory;
public ActivityController(
IActivityDisplayManager displayManager,
IActivityLibrary activityLibrary,
IActivityDisplayManager displayManager,
IActivityLibrary activityLibrary,
IActivityShapeFactory activityShapeFactory)
{
this.displayManager = displayManager;
@ -34,15 +34,32 @@ namespace Flowsharp.Web.ViewComponents.Controllers
this.activityShapeFactory = activityShapeFactory;
}
[HttpPost("create/{activityName}")]
public async Task<IActionResult> Create(string activityName, [FromBody] JToken json, CancellationToken cancellationToken)
{
var activityDescriptor = await activityLibrary.GetActivityByNameAsync(activityName, cancellationToken);
var activityModel = activityDescriptor.InstantiateActivity(json);
activityModel.Id = Guid.NewGuid().ToString();
var editorShape = await displayManager.BuildEditorAsync(activityModel, this, false);
var model = new ActivityEditorViewModel
{
ActivityJson = JsonConvert.SerializeObject(activityModel),
ActivityEditorShape = editorShape,
};
return View("Edit", model);
}
[HttpPost("edit/{activityName}")]
public async Task<IActionResult> Edit(string activityName, [FromBody] string json, CancellationToken cancellationToken)
public async Task<IActionResult> Edit(string activityName, [FromBody] JToken json, CancellationToken cancellationToken)
{
var activityDescriptor = await activityLibrary.GetActivityByNameAsync(activityName, cancellationToken);
var activityModel = activityDescriptor.InstantiateActivity(json);
var editorShape = await displayManager.BuildEditorAsync(activityModel, this, false);
var model = new ActivityEditorViewModel
{
ActivityJson = json,
ActivityJson = json.ToString(),
ActivityEditorShape = editorShape,
};
return View(model);

View file

@ -5,9 +5,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Resources" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Resources" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>
@ -17,4 +17,4 @@
<ProjectReference Include="..\Flowsharp.Web.Abstractions\Flowsharp.Web.Abstractions.csproj" />
</ItemGroup>
</Project>
</Project>

View file

@ -1,9 +1,11 @@
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Flowsharp.Extensions;
using Flowsharp.Web.Abstractions.Services;
using Flowsharp.Web.ViewComponents.Models;
using Newtonsoft.Json.Linq;
using OrchardCore.DisplayManagement;
namespace Flowsharp.Web.ViewComponents.Services
@ -23,7 +25,7 @@ namespace Flowsharp.Web.ViewComponents.Services
{
dynamic shape = await displayManager.BuildDisplayAsync(activity, null, "Design");
var customFields = activity.Metadata.CustomFields;
var designerMetadata = customFields.Get("Designer", () => new ActivityDesignerMetadata());
var designerMetadata = customFields.GetValue("Designer", StringComparison.OrdinalIgnoreCase)?.ToObject<ActivityDesignerMetadata>() ?? new ActivityDesignerMetadata();
var descriptor = await activityLibrary.GetActivityByNameAsync(activity.Name, CancellationToken.None);
shape.Metadata.Type = $"Activity_Design";

View file

@ -30,12 +30,12 @@ namespace Flowsharp.Web.ViewComponents.ViewComponents
return View(viewModel);
}
private async Task<ICollection<IShape>> BuildActivityShapesAsync(IEnumerable<Flowsharp.Models.ActivityDescriptor> descriptors)
private async Task<ICollection<IShape>> BuildActivityShapesAsync(IEnumerable<ActivityDescriptor> descriptors)
{
return await Task.WhenAll(descriptors.Select(BuildActivityShapeAsync));
}
private async Task<IShape> BuildActivityShapeAsync(Flowsharp.Models.ActivityDescriptor descriptor)
private async Task<IShape> BuildActivityShapeAsync(ActivityDescriptor descriptor)
{
var activityShape = await shapeFactory.CreateAsync("Activity_Card", () => Task.FromResult<IShape>(new ActivityDescriptorViewModel(descriptor)));
activityShape.Metadata.Alternates.Add($"Activity_Card__{descriptor.Name}");

View file

@ -3,9 +3,9 @@ using OrchardCore.DisplayManagement.Views;
namespace Flowsharp.Web.ViewComponents.ViewModels
{
public class ActivityDescriptorViewModel : ShapeViewModel<Flowsharp.Models.ActivityDescriptor>
public class ActivityDescriptorViewModel : ShapeViewModel<ActivityDescriptor>
{
public ActivityDescriptorViewModel(Flowsharp.Models.ActivityDescriptor value) : base(value)
public ActivityDescriptorViewModel(ActivityDescriptor value) : base(value)
{
}
}

View file

@ -1,8 +1,3 @@
@using Flowsharp.Web.ViewComponents.Models
@using Microsoft.Extensions.Localization
@using Newtonsoft.Json
@using Newtonsoft.Json.Serialization
@using IActivity = Flowsharp.IActivity
@model dynamic
@{
var activity = (IActivity)Model.Activity;

View file

@ -1,13 +1,16 @@
@model WorkflowDesignerViewModel
@{
var serializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
var workflowMetadataJson = JsonConvert.SerializeObject(Model.Workflow.Metadata, serializerSettings);
}
<div class="workflow-designer-container">
<div class="workflow-canvas">
<div class="workflow-canvas" data-workflow-metadata="@workflowMetadataJson" data-workflow-status="@Model.Workflow.Status">
@foreach (var activityShape in Model.ActivityShapes)
{
@await DisplayAsync(activityShape)
}
</div>
<div data-base-url="@Url.Content("~/")" class="modal fade activity-editor-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style="display: none;">
<div class="modal fade activity-editor-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style="display: none;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">

View file

@ -1,6 +1,10 @@
@using OrchardCore.DisplayManagement.Views;
@using OrchardCore.DisplayManagement.Views
@using Flowsharp.Models
@using Flowsharp.Web.ViewComponents.Models
@using Flowsharp.Web.ViewComponents.ViewModels
@using Microsoft.Extensions.Localization
@using Newtonsoft.Json
@using Newtonsoft.Json.Serialization
@inherits OrchardCore.DisplayManagement.Razor.RazorPage<TModel>
@namespace Flowsharp.Web.ViewComponents.Views
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View file

@ -5,9 +5,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.0.0-beta3-69736" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.ResourceManagement" Version="1.0.0-beta3-69807" />
<PackageReference Include="OrchardCore.Theme.Targets" Version="1.0.0-beta3-69807" />
</ItemGroup>
<ItemGroup>