Initial editor persistence
This commit is contained in:
parent
9819534eee
commit
66cd7c04e6
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace Flowsharp
|
|||
{
|
||||
public interface IActivity
|
||||
{
|
||||
int Id { get; set; }
|
||||
string Id { get; set; }
|
||||
string Name { get; }
|
||||
ActivityMetadata Metadata { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,6 @@ namespace Flowsharp
|
|||
{
|
||||
public interface IActivityProvider
|
||||
{
|
||||
Task<IEnumerable<Models.ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
|
||||
Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace Flowsharp.Models
|
|||
{
|
||||
}
|
||||
|
||||
public TargetEndpoint(Flowsharp.IActivity activity) : base(activity)
|
||||
public TargetEndpoint(IActivity activity) : base(activity)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
public class WorkflowFault
|
||||
{
|
||||
public Flowsharp.IActivity FaultedActivity { get; set; }
|
||||
public IActivity FaultedActivity { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
<header>
|
||||
<h4><i class="fa fa-code-fork"></i>Write Line</h4>
|
||||
<i>"Hello World"</i>
|
||||
<i>"@Model.Activity.TextExpression"</i>
|
||||
</header>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
44
src/web/modules/Flowsharp.Web.Management/Assets/package-lock.json
generated
Normal file
44
src/web/modules/Flowsharp.Web.Management/Assets/package-lock.json
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/web/modules/Flowsharp.Web.Management/Assets/package.json
Normal file
10
src/web/modules/Flowsharp.Web.Management/Assets/package.json
Normal 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": {}
|
||||
}
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
8
src/web/modules/Flowsharp.Web.Management/assets.json
Normal file
8
src/web/modules/Flowsharp.Web.Management/assets.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
{
|
||||
"inputs": [
|
||||
"assets/scripts/workflow-editor.ts"
|
||||
],
|
||||
"output": "wwwroot/workflow-editor.js"
|
||||
}
|
||||
]
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"decode-html": "^2.0.0",
|
||||
"jsplumb": "^2.8.3",
|
||||
"requirejs": "^2.3.6"
|
||||
"jsplumb": "^2.8.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Flowsharp {
|
||||
export interface IActivityMetadata {
|
||||
customFields: any;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
///<reference path="activity-metadata.ts"/>
|
||||
///<reference path="endpoint.ts"/>
|
||||
|
||||
namespace Flowsharp {
|
||||
export interface IActivity {
|
||||
id: string;
|
||||
endpoints: IEndpoint[];
|
||||
metadata: IActivityMetadata;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
namespace Flowsharp {
|
||||
export interface ITransition {
|
||||
export interface IConnection {
|
||||
sourceActivityId: string;
|
||||
sourceOutcomeName: string;
|
||||
destinationActivityId: null;
|
||||
|
|
@ -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'
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
namespace Flowsharp {
|
||||
export interface IWorkflowMetadata {
|
||||
id: string;
|
||||
parentId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
customFields: any;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace Flowsharp {
|
||||
export enum WorkflowStatus {
|
||||
Idle,
|
||||
Starting,
|
||||
Resuming,
|
||||
Executing,
|
||||
Halted,
|
||||
Finished,
|
||||
Faulted,
|
||||
Aborted
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue