diff --git a/Elsa.sln b/Elsa.sln index bbb21e67f..083dddf97 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -67,6 +67,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Email", "sr EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Web.Activities.Email", "src\web\modules\Elsa.Web.Activities.Email\Elsa.Web.Activities.Email.csproj", "{6B5E8D2F-3C4A-407A-B78C-DF4C3B32FF92}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Cron", "src\activities\Elsa.Activities.Cron\Elsa.Activities.Cron.csproj", "{E4B71DC4-3E73-49C3-9B4E-CA13909F222D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Web.Activities.Cron", "src\web\modules\Elsa.Web.Activities.Cron\Elsa.Web.Activities.Cron.csproj", "{FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Runtime.Hosting", "src\core\Elsa.Runtime.Hosting\Elsa.Runtime.Hosting.csproj", "{4728D287-E4F4-4219-8066-924B53E6E17C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +167,18 @@ Global {6B5E8D2F-3C4A-407A-B78C-DF4C3B32FF92}.Debug|Any CPU.Build.0 = Debug|Any CPU {6B5E8D2F-3C4A-407A-B78C-DF4C3B32FF92}.Release|Any CPU.ActiveCfg = Release|Any CPU {6B5E8D2F-3C4A-407A-B78C-DF4C3B32FF92}.Release|Any CPU.Build.0 = Release|Any CPU + {E4B71DC4-3E73-49C3-9B4E-CA13909F222D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4B71DC4-3E73-49C3-9B4E-CA13909F222D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4B71DC4-3E73-49C3-9B4E-CA13909F222D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4B71DC4-3E73-49C3-9B4E-CA13909F222D}.Release|Any CPU.Build.0 = Release|Any CPU + {FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8}.Release|Any CPU.Build.0 = Release|Any CPU + {4728D287-E4F4-4219-8066-924B53E6E17C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4728D287-E4F4-4219-8066-924B53E6E17C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4728D287-E4F4-4219-8066-924B53E6E17C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4728D287-E4F4-4219-8066-924B53E6E17C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -192,6 +210,9 @@ Global {089E23E7-0B8D-4BB6-949D-EB669C116D9E} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} {D20FCB88-9DCA-49EA-9CC2-5B94DD935D30} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} {6B5E8D2F-3C4A-407A-B78C-DF4C3B32FF92} = {B33F1927-FF0A-4826-80C6-F480E9C43892} + {E4B71DC4-3E73-49C3-9B4E-CA13909F222D} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {FE6ECF63-D3FC-44EA-8405-C2A73F7C00C8} = {B33F1927-FF0A-4826-80C6-F480E9C43892} + {4728D287-E4F4-4219-8066-924B53E6E17C} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs b/src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs new file mode 100644 index 000000000..816d5f44c --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs @@ -0,0 +1,12 @@ +using System; +using Elsa.Expressions; +using Elsa.Models; + +namespace Elsa.Activities.Cron.Activities +{ + public class CronTrigger : Activity + { + public WorkflowExpression CronExpression { get; set; } + public DateTime? StartTimestamp { get; set; } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/ActivityDescriptors.cs b/src/activities/Elsa.Activities.Cron/ActivityDescriptors.cs new file mode 100644 index 000000000..9593dc342 --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/ActivityDescriptors.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Elsa.Activities.Cron.Activities; +using Elsa.Models; +using Microsoft.Extensions.Localization; + +namespace Elsa.Activities.Cron +{ + public class ActivityDescriptors : ActivityDescriptorProviderBase + { + public ActivityDescriptors(IStringLocalizer localizer) + { + T = localizer; + } + + private IStringLocalizer T { get; } + private LocalizedString Category => T["Triggers"]; + + protected override IEnumerable Describe() + { + yield return ActivityDescriptor.ForTrigger( + Category, + T["Cron Trigger"], + T["Triggers at specified intervals using CRON expressions."], + T["Done"]); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Drivers/CronTriggerDriver.cs b/src/activities/Elsa.Activities.Cron/Drivers/CronTriggerDriver.cs new file mode 100644 index 000000000..0a8f9df59 --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/Drivers/CronTriggerDriver.cs @@ -0,0 +1,55 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Activities.Cron.Activities; +using Elsa.Handlers; +using Elsa.Models; +using Elsa.Results; +using NCrontab; +using NodaTime; +using NodaTime.Extensions; + +namespace Elsa.Activities.Cron.Drivers +{ + public class CronTriggerDriver : ActivityDriver + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + private readonly IClock clock; + + public CronTriggerDriver(IWorkflowExpressionEvaluator expressionEvaluator, IClock clock) + { + this.expressionEvaluator = expressionEvaluator; + this.clock = clock; + } + + protected override async Task OnCanExecuteAsync(CronTrigger activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) + { + return activity.StartTimestamp == null || await IsExpiredAsync(activity, workflowContext, cancellationToken); + } + + protected override ActivityExecutionResult OnExecute(CronTrigger activity, WorkflowExecutionContext workflowContext) + { + if (activity.StartTimestamp == null) + activity.StartTimestamp = clock.GetCurrentInstant().ToDateTimeUtc(); + + return Halt(); + } + + protected override async Task OnResumeAsync(CronTrigger activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) + { + var isExpired = await IsExpiredAsync(activity, workflowContext, cancellationToken); + + return isExpired ? (ActivityExecutionResult) Endpoint("Done") : Halt(); + } + + private async Task IsExpiredAsync(CronTrigger activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) + { + var cronExpression = await expressionEvaluator.EvaluateAsync(activity.CronExpression, workflowContext, cancellationToken); + var schedule = CrontabSchedule.Parse(cronExpression); + var now = clock.GetCurrentInstant().ToDateTimeUtc(); + var startTimestamp = activity.StartTimestamp ?? now; + var nextOccurrence = schedule.GetNextOccurrence(startTimestamp); + + return now >= nextOccurrence; + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Elsa.Activities.Cron.csproj b/src/activities/Elsa.Activities.Cron/Elsa.Activities.Cron.csproj new file mode 100644 index 000000000..1c48c2927 --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/Elsa.Activities.Cron.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + 1.0.0 + Sipke Schoorstra + Elsa is a set of workflowing libraries and tools to enable super-fast workflowing capabilities in any .NET Core application. + 2019 + https://github.com/sfmskywalker/Elsa + https://github.com/sfmskywalker/Elsa + GitHub + elsa, workflows, orchard + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..104ffbd82 --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,27 @@ +using Elsa.Activities.Cron.Drivers; +using Elsa.Activities.Cron.HostedServices; +using Elsa.Activities.Cron.Options; +using Elsa.Extensions; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Activities.Cron.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddCronDescriptors(this IServiceCollection services) + { + return services.AddActivityDescriptors(); + } + + public static IServiceCollection AddCronDrivers(this IServiceCollection services, IConfiguration configuration) + { + return services + .AddCronDescriptors() + .AddOptions() + .Configure(configuration) + .AddHostedService() + .AddActivityDriver(); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs b/src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs new file mode 100644 index 000000000..9f3d2e60d --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Activities.Cron.Activities; +using Elsa.Activities.Cron.Options; +using Elsa.Models; +using Elsa.Runtime; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Elsa.Activities.Cron.HostedServices +{ + public class CronHostedService : BackgroundService + { + private readonly IWorkflowHost workflowHost; + private readonly IOptions options; + private readonly ILogger logger; + + public CronHostedService(IWorkflowHost workflowHost, IOptions options, ILogger logger) + { + this.workflowHost = workflowHost; + this.options = options; + this.logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + try + { + await workflowHost.TriggerWorkflowAsync(nameof(CronTrigger), Variables.Empty, stoppingToken); + } + catch (Exception ex) + { + logger.LogError(ex, $"Exception occurred while invoking workflows."); + } + + await Task.Delay(options.Value.SweepInterval.ToDuration().ToTimeSpan(), stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Options/CronOptions.cs b/src/activities/Elsa.Activities.Cron/Options/CronOptions.cs new file mode 100644 index 000000000..04094bdc8 --- /dev/null +++ b/src/activities/Elsa.Activities.Cron/Options/CronOptions.cs @@ -0,0 +1,14 @@ +using NodaTime; + +namespace Elsa.Activities.Cron.Options +{ + public class CronOptions + { + public CronOptions() + { + SweepInterval = Period.FromMinutes(1); + } + + public Period SweepInterval { get; set; } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Email/Drivers/SendEmailDriver.cs b/src/activities/Elsa.Activities.Email/Drivers/SendEmailDriver.cs index 93f7033ea..cabe20e4b 100644 --- a/src/activities/Elsa.Activities.Email/Drivers/SendEmailDriver.cs +++ b/src/activities/Elsa.Activities.Email/Drivers/SendEmailDriver.cs @@ -34,7 +34,7 @@ namespace Elsa.Activities.Email.Drivers }; mailMessage.To.Add(to); - smtpClient.SendAsync(mailMessage, null); + await smtpClient.SendMailAsync(mailMessage); return Endpoint("Done"); } diff --git a/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs b/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs index 5a3e8d8f3..b30bd76c6 100644 --- a/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs +++ b/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs @@ -21,7 +21,7 @@ namespace Elsa.Serialization.Tokenizers private readonly ITokenizerInvoker tokenizerInvoker; private readonly IActivityLibrary activityLibrary; private readonly IClock clock; - private readonly JsonSerializerSettings _serializerSettings; + private readonly JsonSerializerSettings serializerSettings; private readonly JsonSerializer jsonSerializer; public WorkflowTokenizer(ITokenizerInvoker tokenizerInvoker, IActivityLibrary activityLibrary, IClock clock) @@ -30,14 +30,14 @@ namespace Elsa.Serialization.Tokenizers this.activityLibrary = activityLibrary; this.clock = clock; - _serializerSettings = new JsonSerializerSettings + serializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() } .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - _serializerSettings.Converters.Add(new StringEnumConverter()); - jsonSerializer = JsonSerializer.Create(_serializerSettings); + serializerSettings.Converters.Add(new StringEnumConverter()); + jsonSerializer = JsonSerializer.Create(serializerSettings); } public async Task TokenizeWorkflowAsync(Workflow value, CancellationToken cancellationToken) @@ -120,7 +120,7 @@ namespace Elsa.Serialization.Tokenizers var name = token["name"].Value(); var descriptor = await GetActivityDescriptorAsync(name, cancellationToken); var activityType = descriptor?.ActivityType ?? typeof(UnknownActivity); - var activity = (IActivity) token.ToObject(activityType); + var activity = (IActivity) jsonSerializer.Deserialize(token.CreateReader(), activityType); activity.Descriptor = descriptor; return activity; @@ -267,7 +267,7 @@ namespace Elsa.Serialization.Tokenizers { var models = (JArray) token["executionLog"] ?? new JArray(); - return JsonConvert.DeserializeObject>(models.ToString(), _serializerSettings); + return JsonConvert.DeserializeObject>(models.ToString(), serializerSettings); } private IEnumerable DeserializeConnections(JToken token, IDictionary activityDictionary) diff --git a/src/core/Elsa.Runtime.Hosting/Elsa.Runtime.Hosting.csproj b/src/core/Elsa.Runtime.Hosting/Elsa.Runtime.Hosting.csproj new file mode 100644 index 000000000..d9ea7eeeb --- /dev/null +++ b/src/core/Elsa.Runtime.Hosting/Elsa.Runtime.Hosting.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/core/Elsa.Runtime.Hosting/Extensions/ProgramExtensions.cs b/src/core/Elsa.Runtime.Hosting/Extensions/ProgramExtensions.cs new file mode 100644 index 000000000..8dca514d7 --- /dev/null +++ b/src/core/Elsa.Runtime.Hosting/Extensions/ProgramExtensions.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using Elsa.Runtime.Hosting.TypeConverters; +using NodaTime; + +namespace Elsa.Runtime.Hosting.Extensions +{ + public static class ProgramExtensions + { + public static void ConfigureTypeConverters() + { + TypeDescriptor.AddAttributes( + typeof(Period), + new TypeConverterAttribute(typeof(PeriodTypeConverter))); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Runtime.Hosting/TypeConverters/PeriodTypeConverter.cs b/src/core/Elsa.Runtime.Hosting/TypeConverters/PeriodTypeConverter.cs new file mode 100644 index 000000000..c12d59ea6 --- /dev/null +++ b/src/core/Elsa.Runtime.Hosting/TypeConverters/PeriodTypeConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel; +using System.Globalization; +using NodaTime.Text; + +namespace Elsa.Runtime.Hosting.TypeConverters +{ + public class PeriodTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string s) + { + return PeriodPattern.Roundtrip.Parse(s).Value; + } + + return base.ConvertFrom(context, culture, value); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs index db9ebcc98..03f5bac59 100644 --- a/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ namespace Elsa.Runtime.Extensions return services .AddWorkflowsCore() .AddWorkflowsInvoker() - .AddScoped(); + .AddSingleton(); } } } \ No newline at end of file diff --git a/src/core/Elsa.Runtime/WorkflowHost.cs b/src/core/Elsa.Runtime/WorkflowHost.cs index 5cc0c165e..95d53ed05 100644 --- a/src/core/Elsa.Runtime/WorkflowHost.cs +++ b/src/core/Elsa.Runtime/WorkflowHost.cs @@ -31,6 +31,7 @@ namespace Elsa.Runtime public async Task StartWorkflowAsync(Workflow workflow, IActivity startActivity, Variables arguments, CancellationToken cancellationToken) { var workflowInstance = await workflowSerializer.DeriveAsync(workflow, cancellationToken); + startActivity = workflowInstance.Activities.Single(x => x.Id == startActivity.Id); return await invoker.InvokeAsync(workflowInstance, startActivity, arguments, cancellationToken); } diff --git a/src/samples/SampleDashboard.Web/SampleDashboard.Web.csproj b/src/samples/SampleDashboard.Web/SampleDashboard.Web.csproj index bad236750..3ddca60de 100644 --- a/src/samples/SampleDashboard.Web/SampleDashboard.Web.csproj +++ b/src/samples/SampleDashboard.Web/SampleDashboard.Web.csproj @@ -24,6 +24,7 @@ + diff --git a/src/samples/SampleHost.Web/Program.cs b/src/samples/SampleHost.Web/Program.cs index 2bb7429ac..aef616675 100644 --- a/src/samples/SampleHost.Web/Program.cs +++ b/src/samples/SampleHost.Web/Program.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Elsa.Runtime.Hosting.Extensions; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; @@ -13,7 +14,10 @@ namespace SampleHost.Web await host.RunAsync(); } - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args); + public static IWebHostBuilder CreateWebHostBuilder(string[] args) + { + ProgramExtensions.ConfigureTypeConverters(); + return WebHost.CreateDefaultBuilder(args); + } } } \ No newline at end of file diff --git a/src/samples/SampleHost.Web/SampleHost.Web.csproj b/src/samples/SampleHost.Web/SampleHost.Web.csproj index b79d9697f..9e2ee58e1 100644 --- a/src/samples/SampleHost.Web/SampleHost.Web.csproj +++ b/src/samples/SampleHost.Web/SampleHost.Web.csproj @@ -20,11 +20,13 @@ + + diff --git a/src/samples/SampleHost.Web/Startup.cs b/src/samples/SampleHost.Web/Startup.cs index 3114b10d6..4aa722c4c 100644 --- a/src/samples/SampleHost.Web/Startup.cs +++ b/src/samples/SampleHost.Web/Startup.cs @@ -1,4 +1,5 @@ using Elsa.Activities.Console.Extensions; +using Elsa.Activities.Cron.Extensions; using Elsa.Activities.Email.Extensions; using Elsa.Activities.Http.Extensions; using Elsa.Activities.Primitives.Extensions; @@ -31,7 +32,8 @@ namespace SampleHost.Web .AddPrimitiveWorkflowDrivers() .AddConsoleWorkflowDrivers() .AddHttpWorkflowDrivers() - .AddEmailDrivers(Configuration.GetSection("Smtp")); + .AddEmailDrivers(Configuration.GetSection("Smtp")) + .AddCronDrivers(Configuration.GetSection("Cron")); services .AddMvc() diff --git a/src/samples/SampleHost.Web/appsettings.json b/src/samples/SampleHost.Web/appsettings.json index 7a7b539b1..cbe57a560 100644 --- a/src/samples/SampleHost.Web/appsettings.json +++ b/src/samples/SampleHost.Web/appsettings.json @@ -15,5 +15,8 @@ "Credentials": { "Type": "None" } + }, + "Cron": { + "SweepInterval": "PT10S" } } diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Display/CronTriggerDisplay.cs b/src/web/modules/Elsa.Web.Activities.Cron/Display/CronTriggerDisplay.cs new file mode 100644 index 000000000..f4ef49441 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Display/CronTriggerDisplay.cs @@ -0,0 +1,20 @@ +using Elsa.Activities.Cron.Activities; +using Elsa.Web.Activities.Cron.ViewModels; +using Elsa.Web.Components.ViewModels; +using Elsa.Web.Drivers; + +namespace Elsa.Web.Activities.Cron.Display +{ + public class CronTriggerDisplay : ActivityDisplayDriver + { + protected override void EditActivity(CronTrigger activity, CronTriggerViewModel model) + { + model.CronExpression = new ExpressionViewModel(activity.CronExpression); + } + + protected override void UpdateActivity(CronTriggerViewModel model, CronTrigger activity) + { + activity.CronExpression = model.CronExpression.ToWorkflowExpression(); + } + } +} \ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Elsa.Web.Activities.Cron.csproj b/src/web/modules/Elsa.Web.Activities.Cron/Elsa.Web.Activities.Cron.csproj new file mode 100644 index 000000000..fbb87800c --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Elsa.Web.Activities.Cron.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + 1.0.0 + Sipke Schoorstra + Elsa is a set of workflowing libraries and tools to enable super-fast workflowing capabilities in any .NET Core application. + 2019 + https://github.com/sfmskywalker/Elsa + https://github.com/sfmskywalker/Elsa + GitHub + elsa, workflows, orchard + + + + + + + + + + + + + diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Manifest.cs b/src/web/modules/Elsa.Web.Activities.Cron/Manifest.cs new file mode 100644 index 000000000..25cf183c7 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Manifest.cs @@ -0,0 +1,7 @@ +using OrchardCore.Modules.Manifest; + +[assembly: Module( + Name = "Cron Activities", + Category = "Elsa Workflows", + Description = "Provides a Cron Trigger activity." +)] diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Startup.cs b/src/web/modules/Elsa.Web.Activities.Cron/Startup.cs new file mode 100644 index 000000000..8089d65f9 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Startup.cs @@ -0,0 +1,18 @@ +using Elsa.Activities.Cron.Extensions; +using Elsa.Web.Activities.Cron.Display; +using Elsa.Web.Extensions; +using Microsoft.Extensions.DependencyInjection; +using OrchardCore.Modules; + +namespace Elsa.Web.Activities.Cron +{ + public class Startup : StartupBase + { + public override void ConfigureServices(IServiceCollection services) + { + services + .AddCronDescriptors() + .AddActivityDisplay(); + } + } +} \ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/ViewModels/CronTriggerViewModel.cs b/src/web/modules/Elsa.Web.Activities.Cron/ViewModels/CronTriggerViewModel.cs new file mode 100644 index 000000000..5d331399d --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/ViewModels/CronTriggerViewModel.cs @@ -0,0 +1,9 @@ +using Elsa.Web.Components.ViewModels; + +namespace Elsa.Web.Activities.Cron.ViewModels +{ + public class CronTriggerViewModel + { + public ExpressionViewModel CronExpression { get; set; } + } +} \ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Card.cshtml b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Card.cshtml new file mode 100644 index 000000000..04bf18023 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Card.cshtml @@ -0,0 +1,6 @@ +@model ActivityShapeModel +@{ + var activityDescriptor = Model.Activity.Descriptor; +} +

@activityDescriptor.DisplayText

+

@activityDescriptor.Description

\ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Design.cshtml b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Design.cshtml new file mode 100644 index 000000000..b04d8fe9b --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Design.cshtml @@ -0,0 +1,6 @@ +@model ActivityShapeModel + +
+

@Model.DisplayText

+ "@($"{Model.Activity.CronExpression}")" +
\ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Edit.cshtml b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Edit.cshtml new file mode 100644 index 000000000..42e1b86d0 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Views/CronTrigger.Edit.cshtml @@ -0,0 +1,7 @@ +@model CronTriggerViewModel +
+ + + + @T["Enter a crontab expression. See https://crontab.guru/."] +
\ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Activities.Cron/Views/_ViewImports.cshtml b/src/web/modules/Elsa.Web.Activities.Cron/Views/_ViewImports.cshtml new file mode 100644 index 000000000..92e10d086 --- /dev/null +++ b/src/web/modules/Elsa.Web.Activities.Cron/Views/_ViewImports.cshtml @@ -0,0 +1,7 @@ +@using Elsa.Web.ViewModels +@using Elsa.Web.Activities.Cron.ViewModels +@using Elsa.Activities.Cron.Activities +@using OrchardCore.Mvc.Core.Utilities +@inherits OrchardCore.DisplayManagement.Razor.RazorPage +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Elsa.Web.Components \ No newline at end of file