mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-01-24 05:42:10 +08:00
Handling workflows triggered by multiple events
--HG-- branch : 1.x extra : rebase_source : 65f8932e645c67f3fb5220d4314aaaea01c93719
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class BranchActivity : Task {
|
||||
|
||||
public BranchActivity() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override bool CanExecute(ActivityContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
return GetBranches(context).Select(x => T(x));
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
return GetBranches(context).Select(x => T(x));
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return "Branch"; }
|
||||
}
|
||||
|
||||
public override LocalizedString Category {
|
||||
get { return T("Flow"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Splits the workflow on two different branches."); }
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
get {
|
||||
return "ActivityBranch";
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetBranches(ActivityContext context) {
|
||||
if (context.State == null) {
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
string branches = context.State.Branches;
|
||||
|
||||
if (String.IsNullOrEmpty(branches)) {
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
return branches.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,14 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public abstract class ContentActivity : BlockingActivity {
|
||||
public abstract class ContentActivity : Event {
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override bool CanStartWorkflow {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public override bool CanExecute(ActivityContext context) {
|
||||
try {
|
||||
|
||||
@@ -40,8 +44,8 @@ namespace Orchard.Workflows.Activities {
|
||||
return new[] { T("Done") };
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
return T("Done");
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
yield return T("Done");
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
@@ -60,7 +64,6 @@ namespace Orchard.Workflows.Activities {
|
||||
get { return "ContentCreated"; }
|
||||
}
|
||||
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Content is created."); }
|
||||
}
|
||||
@@ -93,7 +96,6 @@ namespace Orchard.Workflows.Activities {
|
||||
get { return "ContentRemoved"; }
|
||||
}
|
||||
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Content is removed."); }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class DecisionActivity : BaseActivity {
|
||||
public class DecisionActivity : Task {
|
||||
|
||||
public DecisionActivity() {
|
||||
T = NullLocalizer.Instance;
|
||||
@@ -28,8 +28,8 @@ namespace Orchard.Workflows.Activities {
|
||||
return new[] { T("True"), T("False") };
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
return T("True");
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
yield return T("True");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class IsInRoleActivity : BaseActivity {
|
||||
public class IsInRoleActivity : Task {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public IsInRoleActivity(IWorkContextAccessor workContextAccessor) {
|
||||
@@ -41,13 +41,13 @@ namespace Orchard.Workflows.Activities {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
|
||||
if (UserIsInRole(context)) {
|
||||
return T("Yes");
|
||||
yield return T("Yes");
|
||||
}
|
||||
|
||||
return T("No");
|
||||
yield return T("No");
|
||||
}
|
||||
|
||||
private bool UserIsInRole(ActivityContext context) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Events;
|
||||
using Orchard.Messaging.Events;
|
||||
using Orchard.Messaging.Models;
|
||||
using Orchard.Messaging.Services;
|
||||
@@ -12,9 +11,9 @@ using Orchard.Security;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities.Mail {
|
||||
namespace Orchard.Workflows.Activities {
|
||||
|
||||
public class MailActions : BaseActivity {
|
||||
public class MailActions : Task {
|
||||
private readonly IMessageManager _messageManager;
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
private readonly IMembershipService _membershipService;
|
||||
@@ -55,12 +54,13 @@ namespace Orchard.Workflows.Activities.Mail {
|
||||
get { return T("Sends an e-mail to a specific user."); }
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
string recipient = context.State.Recipient;
|
||||
var properties = new Dictionary<string, string>(); // context.State.Properties
|
||||
|
||||
properties.Add("Body", context.State.Body.ToString());
|
||||
properties.Add("Subject", context.State.Subject.ToString());
|
||||
var properties = new Dictionary<string, string> {
|
||||
{"Body", context.State.Body.ToString()},
|
||||
{"Subject", context.State.Subject.ToString()}
|
||||
};
|
||||
|
||||
if (recipient == "owner") {
|
||||
var content = context.Tokens["Content"] as IContent;
|
||||
@@ -95,7 +95,7 @@ namespace Orchard.Workflows.Activities.Mail {
|
||||
_messageManager.Send(SplitEmail(email), MessageType, "email", properties);
|
||||
}
|
||||
|
||||
return T("Sent");
|
||||
yield return T("Sent");
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitEmail(string commaSeparated) {
|
||||
@@ -7,7 +7,7 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class NotificationActivity : BaseActivity {
|
||||
public class NotificationActivity : Task {
|
||||
private readonly INotifier _notifier;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Orchard.Workflows.Activities {
|
||||
yield return T("Done");
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
string notification = context.State.Notification;
|
||||
string message = context.State.Message;
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Orchard.Workflows.Activities {
|
||||
var notificationType = (NotifyType)Enum.Parse(typeof(NotifyType), notification);
|
||||
_notifier.Add(notificationType, T(message));
|
||||
|
||||
return T("Done");
|
||||
yield return T("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class PublishActivity : BaseActivity {
|
||||
public class PublishActivity : Task {
|
||||
private readonly IContentManager _contentManager;
|
||||
|
||||
public PublishActivity(IContentManager contentManager) {
|
||||
@@ -22,9 +22,9 @@ namespace Orchard.Workflows.Activities {
|
||||
return new[] { T("Published") };
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
_contentManager.Publish(context.Content.ContentItem);
|
||||
return T("Published");
|
||||
yield return T("Published");
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Services;
|
||||
using Orchard.Tasks;
|
||||
using Orchard.Workflows.Models;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class TimerActivity : Event {
|
||||
private readonly IClock _clock;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public TimerActivity(
|
||||
IClock clock,
|
||||
IWorkContextAccessor workContextAccessor) {
|
||||
_clock = clock;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override string Name {
|
||||
get { return "Timer"; }
|
||||
}
|
||||
|
||||
public override LocalizedString Category {
|
||||
get { return T("Tasks"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Wait for a specific time has passed."); }
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
get { return "ActivityTimer"; }
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
yield return T("Done");
|
||||
}
|
||||
|
||||
public override bool CanExecute(ActivityContext context) {
|
||||
|
||||
return _clock.UtcNow > When(context);
|
||||
}
|
||||
|
||||
public override void Touch(dynamic workflowState) {
|
||||
workflowState.TimerActivity_StartedUtc = _clock.UtcNow;
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
if(_clock.UtcNow > When(context)) {
|
||||
yield return T("Done");
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime When(ActivityContext context) {
|
||||
try {
|
||||
int amount = context.State.Amount;
|
||||
string type = context.State.Unity;
|
||||
|
||||
DateTime started = context.WorkflowState.TimerActivity_StartedUtc;
|
||||
|
||||
var when = started;
|
||||
|
||||
switch (type) {
|
||||
case "Minute":
|
||||
when = when.AddMinutes(amount);
|
||||
break;
|
||||
case "Hour":
|
||||
when = when.AddHours(amount);
|
||||
break;
|
||||
case "Day":
|
||||
when = when.AddDays(amount);
|
||||
break;
|
||||
case "Week":
|
||||
when = when.AddDays(7*amount);
|
||||
break;
|
||||
case "Month":
|
||||
when = when.AddMonths(amount);
|
||||
break;
|
||||
case "Year":
|
||||
when = when.AddYears(amount);
|
||||
break;
|
||||
}
|
||||
|
||||
return when;
|
||||
}
|
||||
catch {
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TimerBackgroundTask : IBackgroundTask {
|
||||
private readonly IClock _clock;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IWorkflowManager _workflowManager;
|
||||
private readonly IRepository<AwaitingActivityRecord> _awaitingActivityRepository;
|
||||
|
||||
public TimerBackgroundTask(
|
||||
IClock clock,
|
||||
IContentManager contentManager,
|
||||
IWorkflowManager workflowManager,
|
||||
IRepository<AwaitingActivityRecord> awaitingActivityRepository) {
|
||||
_clock = clock;
|
||||
_contentManager = contentManager;
|
||||
_workflowManager = workflowManager;
|
||||
_awaitingActivityRepository = awaitingActivityRepository;
|
||||
}
|
||||
|
||||
public void Sweep() {
|
||||
var awaiting = _awaitingActivityRepository.Table.Where(x => x.ActivityRecord.Name == "Timer").ToList();
|
||||
var actions = awaiting.Where(x => {
|
||||
var contentItem = _contentManager.Get(x.ContentItemRecord.Id, VersionOptions.Latest);
|
||||
var state = FormParametersHelper.FromJsonString(x.ActivityRecord.State);
|
||||
var workflowState = FormParametersHelper.FromJsonString(x.WorkflowRecord.State);
|
||||
return _clock.UtcNow > TimerActivity.When(new ActivityContext {
|
||||
State = state,
|
||||
WorkflowState = workflowState,
|
||||
Content = contentItem
|
||||
});
|
||||
});
|
||||
|
||||
foreach (var action in actions) {
|
||||
var contentItem = _contentManager.Get(action.ContentItemRecord.Id, VersionOptions.Latest);
|
||||
_workflowManager.TriggerEvent("Timer", contentItem, () => new Dictionary<string, object> { { "Content", contentItem } });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class UserTaskActivity : BlockingActivity {
|
||||
public class UserTaskActivity : Event {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public UserTaskActivity(IWorkContextAccessor workContextAccessor) {
|
||||
@@ -43,13 +43,11 @@ namespace Orchard.Workflows.Activities {
|
||||
return ActionIsValid(context) && UserIsInRole(context);
|
||||
}
|
||||
|
||||
public override LocalizedString Execute(ActivityContext context) {
|
||||
public override IEnumerable<LocalizedString> Execute(ActivityContext context) {
|
||||
|
||||
if (ActionIsValid(context) && UserIsInRole(context)) {
|
||||
return T(context.Tokens["UserTask.Action"].ToString());
|
||||
yield return T(context.Tokens["UserTask.Action"].ToString());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool UserIsInRole(ActivityContext context) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class BranchForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public BranchForms(IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, dynamic> form =
|
||||
shape => {
|
||||
|
||||
var f = Shape.Form(
|
||||
Id: "BranchNames",
|
||||
_Message: Shape.Textbox(
|
||||
Id: "branches", Name: "Branches",
|
||||
Title: T("Available branches."),
|
||||
Description: T("A comma separated list of names."),
|
||||
Classes: new[] {"textMedium"})
|
||||
);
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form("ActivityBranch", form);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class ContentForms : IFormProvider {
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
protected dynamic Shape { get; set; }
|
||||
@@ -3,7 +3,7 @@ using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Activities.Mail {
|
||||
namespace Orchard.Workflows.Forms {
|
||||
|
||||
public class MailForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
@@ -3,7 +3,7 @@ using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class NotificationActivityForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
@@ -6,7 +6,7 @@ using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Roles.Services;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class SelectRolesForms : IFormProvider {
|
||||
private readonly IRoleService _roleService;
|
||||
protected dynamic Shape { get; set; }
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class ScheduleForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ScheduleForms(IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
context.Form("ActivityTimer",
|
||||
shape => {
|
||||
var form = Shape.Form(
|
||||
Id: "ActionDelay",
|
||||
_Amount: Shape.Textbox(
|
||||
Id: "Amount", Name: "Amount",
|
||||
Title: T("Amount"),
|
||||
Classes: new[] { "text-small" }),
|
||||
_Type: Shape.SelectList(
|
||||
Id: "Unity", Name: "Unity",
|
||||
Title: T("Amount type"))
|
||||
.Add(new SelectListItem { Value = "Minute", Text = T("Minutes").Text, Selected = true })
|
||||
.Add(new SelectListItem { Value = "Hour", Text = T("Hours").Text })
|
||||
.Add(new SelectListItem { Value = "Day", Text = T("Days").Text })
|
||||
.Add(new SelectListItem { Value = "Week", Text = T("Weeks").Text })
|
||||
.Add(new SelectListItem { Value = "Month", Text = T("Months").Text })
|
||||
);
|
||||
|
||||
return form;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public class ScheduleFormsValitator : FormHandler {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override void Validating(ValidatingContext context) {
|
||||
if (context.FormName == "ActivityTimer") {
|
||||
if (context.ValueProvider.GetValue("Amount").AttemptedValue == String.Empty) {
|
||||
context.ModelState.AddModelError("Amount", T("You must provide an Amount").Text);
|
||||
}
|
||||
|
||||
if (context.ValueProvider.GetValue("Unity").AttemptedValue == String.Empty) {
|
||||
context.ModelState.AddModelError("Unity", T("You must provide a Type").Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Roles.Services;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
namespace Orchard.Workflows.Forms {
|
||||
public class UserTaskForms : IFormProvider {
|
||||
private readonly IRoleService _roleService;
|
||||
protected dynamic Shape { get; set; }
|
||||
@@ -71,6 +71,8 @@
|
||||
<Content Include="Scripts\jquery.jsPlumb-1.3.16-all-min.js" />
|
||||
<Content Include="Scripts\orchard-workflows-serialize.js" />
|
||||
<Content Include="Scripts\orchard-workflows.js" />
|
||||
<Content Include="Styles\workflows-activity-timer.css" />
|
||||
<Content Include="Styles\workflows-activity-branch.css" />
|
||||
<Content Include="Styles\orchard-workflows-admin.css" />
|
||||
<Content Include="Styles\admin-usertask.css" />
|
||||
<Content Include="Styles\workflows-activity-isinrole.css" />
|
||||
@@ -114,9 +116,13 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\ContentActivity.cs" />
|
||||
<Compile Include="Activities\BranchActivity.cs" />
|
||||
<Compile Include="Activities\IsInRoleActivity.cs" />
|
||||
<Compile Include="Activities\Mail\MailActivity.cs" />
|
||||
<Compile Include="Activities\Mail\MailForms.cs" />
|
||||
<Compile Include="Activities\MailActivity.cs" />
|
||||
<Compile Include="Activities\TimerActivity.cs" />
|
||||
<Compile Include="Forms\TimerForms.cs" />
|
||||
<Compile Include="Forms\BranchForms.cs" />
|
||||
<Compile Include="Forms\MailForms.cs" />
|
||||
<Compile Include="Activities\PublishActivity.cs" />
|
||||
<Compile Include="Activities\UserTaskActivity.cs" />
|
||||
<Compile Include="Activities\DecisionActivity.cs" />
|
||||
@@ -138,12 +144,10 @@
|
||||
<Compile Include="Models\WorkflowRecord.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\WorkflowDefinitionRecord.cs" />
|
||||
<Compile Include="Providers\ContentActivityProvider.cs" />
|
||||
<Compile Include="Providers\ContentActivityForms.cs" />
|
||||
<Compile Include="Providers\SelectRolesForms.cs" />
|
||||
<Compile Include="Providers\UserTaskForms.cs" />
|
||||
<Compile Include="Providers\NotificationActivityForms.cs" />
|
||||
<Compile Include="Providers\NotificationActivityProvider.cs" />
|
||||
<Compile Include="Forms\ContentActivityForms.cs" />
|
||||
<Compile Include="Forms\SelectRolesForms.cs" />
|
||||
<Compile Include="Forms\UserTaskForms.cs" />
|
||||
<Compile Include="Forms\NotificationActivityForms.cs" />
|
||||
<Compile Include="ResourceManifest.cs" />
|
||||
<Compile Include="Services\BaseActivity.cs" />
|
||||
<Compile Include="Services\BlockingActivity.cs" />
|
||||
@@ -169,9 +173,6 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Create.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-ContentCreated.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity.cshtml" />
|
||||
</ItemGroup>
|
||||
@@ -205,6 +206,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-SendEmail.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-Branch.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-Timer.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
|
||||
public class ContentActivityProvider : IActivityProvider {
|
||||
public ContentActivityProvider() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeActivityContext describe) {
|
||||
describe.For("Content", T("Content Items"), T("Content Items"))
|
||||
.Element("Created", T("Content Created"), T("Content is actually created."), ContentHasPart, context => T("When content with types ({0}) is created.", FormatPartsList(context)), "SelectContentTypes")
|
||||
.Element("Versioned", T("Content Versioned"), T("Content is actually versioned."), ContentHasPart, context => T("When content with types ({0}) is versioned.", FormatPartsList(context)), "SelectContentTypes")
|
||||
.Element("Published", T("Content Published"), T("Content is actually published."), ContentHasPart, context => T("When content with types ({0}) is published.", FormatPartsList(context)), "SelectContentTypes")
|
||||
.Element("Removed", T("Content Removed"), T("Content is actually removed."), ContentHasPart, context => T("When content with types ({0}) is removed.", FormatPartsList(context)), "SelectContentTypes");
|
||||
}
|
||||
|
||||
private string FormatPartsList(ActivityContext context) {
|
||||
string contenttypes = context.State.ContentTypes;
|
||||
|
||||
if (String.IsNullOrEmpty(contenttypes)) {
|
||||
return T("Any").Text;
|
||||
}
|
||||
|
||||
return contenttypes;
|
||||
}
|
||||
|
||||
private bool ContentHasPart(ActivityContext context) {
|
||||
string contenttypes = context.State.ContentTypes;
|
||||
var content = context.Tokens["Content"] as IContent;
|
||||
|
||||
// "" means 'any'
|
||||
if (String.IsNullOrEmpty(contenttypes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var contentTypes = contenttypes.Split(new[] { ',' });
|
||||
|
||||
return contentTypes.Any(contentType => content.ContentItem.TypeDefinition.Name == contentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
public class NotificationActivityProvider : IActivityProvider {
|
||||
private readonly INotifier _notifier;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public NotificationActivityProvider(INotifier notifier, ITokenizer tokenizer) {
|
||||
_notifier = notifier;
|
||||
_tokenizer = tokenizer;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeActivityContext describe) {
|
||||
describe.For("Notification", T("Notification"), T("Notifications"))
|
||||
.Element(
|
||||
"Notify",
|
||||
T("Notify"),
|
||||
T("Display a message."),
|
||||
ExecuteActivity,
|
||||
DisplayActivity,
|
||||
"ActivityNotify"
|
||||
);
|
||||
}
|
||||
|
||||
private bool ExecuteActivity(ActivityContext context) {
|
||||
string notification = context.State.Notification;
|
||||
string message = context.State.Message;
|
||||
|
||||
message = _tokenizer.Replace(message, context.Tokens);
|
||||
|
||||
var notificationType = (NotifyType)Enum.Parse(typeof(NotifyType), notification);
|
||||
_notifier.Add(notificationType, T(message));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private LocalizedString DisplayActivity(ActivityContext context) {
|
||||
return T("Displays \"{1}\" as {0}", T(context.State.Notification).Text, T(context.State.Message).Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@
|
||||
target = $(target);
|
||||
|
||||
// start button
|
||||
$('#activity-toolbar-start').toggle(target.hasClass('blocking'));
|
||||
$('#activity-toolbar-start').toggle(target.hasClass('canStart'));
|
||||
$('#activity-toolbar-start-checkbox').prop('checked', target.get(0).viewModel.start);
|
||||
|
||||
// edit button
|
||||
|
||||
@@ -3,16 +3,18 @@ using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public abstract class BaseActivity : IActivity {
|
||||
public abstract class Task : IActivity {
|
||||
|
||||
public abstract string Name { get; }
|
||||
public abstract LocalizedString Category { get; }
|
||||
public abstract LocalizedString Description { get; }
|
||||
|
||||
public virtual bool IsBlocking {
|
||||
public virtual bool IsEvent {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool CanStartWorkflow { get { return false; } }
|
||||
|
||||
public virtual string Form {
|
||||
get { return null; }
|
||||
}
|
||||
@@ -23,6 +25,11 @@ namespace Orchard.Workflows.Services {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract LocalizedString Execute(ActivityContext context);
|
||||
public abstract IEnumerable<LocalizedString> Execute(ActivityContext context);
|
||||
|
||||
public virtual void Touch(dynamic state) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@ using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public abstract class BlockingActivity : IActivity {
|
||||
public abstract class Event : IActivity {
|
||||
|
||||
public abstract string Name { get; }
|
||||
public abstract LocalizedString Category { get; }
|
||||
public abstract LocalizedString Description { get; }
|
||||
|
||||
public virtual bool IsBlocking {
|
||||
public virtual bool IsEvent {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
@@ -17,12 +17,20 @@ namespace Orchard.Workflows.Services {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public virtual bool CanStartWorkflow {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
|
||||
|
||||
public virtual bool CanExecute(ActivityContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract LocalizedString Execute(ActivityContext context);
|
||||
public abstract IEnumerable<LocalizedString> Execute(ActivityContext context);
|
||||
|
||||
public virtual void Touch(dynamic workflowState) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,12 @@ namespace Orchard.Workflows.Services {
|
||||
string Name { get; }
|
||||
LocalizedString Category { get; }
|
||||
LocalizedString Description { get; }
|
||||
bool IsBlocking { get; }
|
||||
bool IsEvent { get; }
|
||||
bool CanStartWorkflow { get; }
|
||||
string Form { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of possible outcomes when the activity is executed
|
||||
/// List of possible outcomes when the activity is executed.
|
||||
/// </summary>
|
||||
IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
|
||||
|
||||
@@ -25,6 +26,12 @@ namespace Orchard.Workflows.Services {
|
||||
/// <summary>
|
||||
/// Executes the current activity
|
||||
/// </summary>
|
||||
LocalizedString Execute(ActivityContext context);
|
||||
/// <returns>The names of the resulting outcomes.</returns>
|
||||
IEnumerable<LocalizedString> Execute(ActivityContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Called on blocking activities when they are reached
|
||||
/// </summary>
|
||||
void Touch(dynamic workflowState);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Orchard.Workflows.Services {
|
||||
/// <param name="tokensContext">An object containing the tokens context</param>
|
||||
void TriggerEvent(string name, IContent target, Func<Dictionary<string, object>> tokensContext);
|
||||
|
||||
ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState);
|
||||
IEnumerable<ActivityRecord> ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,7 +53,9 @@ namespace Orchard.Workflows.Services {
|
||||
|
||||
var startedWorkflows = new List<ActivityRecord>();
|
||||
|
||||
// look for workflow definitions with a corresponding starting activity,
|
||||
// look for workflow definitions with a corresponding starting activity
|
||||
// it's important to return activities at this point and not workflows,
|
||||
// as a workflow definition could have multiple entry points with the same type of activity
|
||||
startedWorkflows.AddRange(_activityRepository.Table.Where(
|
||||
x =>x.Name == name && x.Start && x.WorkflowDefinitionRecord.Enabled
|
||||
)
|
||||
@@ -62,6 +64,8 @@ namespace Orchard.Workflows.Services {
|
||||
var awaitingActivities = new List<AwaitingActivityRecord>();
|
||||
|
||||
// and any running workflow paused on this kind of activity for this content
|
||||
// it's important to return activities at this point as a workflow could be awaiting
|
||||
// on several ones. When an activity is restarted, all the other ones of the same workflow are cancelled.
|
||||
awaitingActivities.AddRange(_awaitingActivityRepository.Table.Where(
|
||||
x => x.ActivityRecord.Name == name && x.ActivityRecord.Start == false && x.ContentItemRecord == target.ContentItem.Record
|
||||
).ToList()
|
||||
@@ -133,10 +137,10 @@ namespace Orchard.Workflows.Services {
|
||||
|
||||
private void StartWorkflow(ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens) {
|
||||
var workflowState = FormParametersHelper.FromJsonString("{}");
|
||||
var lastActivity = ExecuteWorkflow(activityRecord.WorkflowDefinitionRecord, activityRecord, target, tokens, workflowState);
|
||||
IEnumerable<ActivityRecord> blockedOn = ExecuteWorkflow(activityRecord.WorkflowDefinitionRecord, activityRecord, target, tokens, workflowState);
|
||||
|
||||
// is the workflow halted on a blocking activity ?
|
||||
if (lastActivity == null) {
|
||||
if (blockedOn == null || !blockedOn.Any()) {
|
||||
// no, nothing to do
|
||||
}
|
||||
else {
|
||||
@@ -148,61 +152,83 @@ namespace Orchard.Workflows.Services {
|
||||
|
||||
_workflowRepository.Create(workflow);
|
||||
|
||||
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
|
||||
ActivityRecord = lastActivity,
|
||||
ContentItemRecord = target.ContentItem.Record
|
||||
});
|
||||
foreach (var blocking in blockedOn) {
|
||||
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
|
||||
ActivityRecord = blocking,
|
||||
ContentItemRecord = target.ContentItem.Record
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ResumeWorkflow(AwaitingActivityRecord awaitingActivityRecord, IContent target, Dictionary<string, object> tokens) {
|
||||
var workflowState = FormParametersHelper.FromJsonString(awaitingActivityRecord.WorkflowRecord.State);
|
||||
var lastActivity = ExecuteWorkflow(awaitingActivityRecord.WorkflowRecord.WorkflowDefinitionRecord, awaitingActivityRecord.ActivityRecord, target, tokens, workflowState);
|
||||
IEnumerable<ActivityRecord> blockedOn = ExecuteWorkflow(awaitingActivityRecord.WorkflowRecord.WorkflowDefinitionRecord, awaitingActivityRecord.ActivityRecord, target, tokens, workflowState);
|
||||
|
||||
// is the workflow halted on a blocking activity ?
|
||||
if (lastActivity == null) {
|
||||
if (blockedOn == null || !blockedOn.Any()) {
|
||||
// no, delete the workflow
|
||||
_workflowRepository.Delete(awaitingActivityRecord.WorkflowRecord);
|
||||
}
|
||||
else {
|
||||
// workflow halted, save state
|
||||
awaitingActivityRecord.ActivityRecord = lastActivity;
|
||||
// remove all previous awaiting activities
|
||||
var workflow = awaitingActivityRecord.WorkflowRecord;
|
||||
workflow.State = FormParametersHelper.ToJsonString(workflowState);
|
||||
workflow.AwaitingActivities.Clear();
|
||||
|
||||
// add the new ones
|
||||
foreach (var blocking in blockedOn) {
|
||||
workflow.AwaitingActivities.Add(new AwaitingActivityRecord {
|
||||
ActivityRecord = blocking,
|
||||
ContentItemRecord = target.ContentItem.Record
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ActivityRecord ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState) {
|
||||
public IEnumerable<ActivityRecord> ExecuteWorkflow(WorkflowDefinitionRecord workflowDefinitionRecord, ActivityRecord activityRecord, IContent target, Dictionary<string, object> tokens, dynamic workflowState) {
|
||||
var firstPass = true;
|
||||
var pending = new Stack<ActivityRecord>();
|
||||
pending.Push(activityRecord);
|
||||
|
||||
var blocking = new List<ActivityRecord>();
|
||||
|
||||
while (pending.Any()) {
|
||||
|
||||
activityRecord = pending.Pop();
|
||||
|
||||
while (true) {
|
||||
// while there is an activity to process
|
||||
var activity = _activitiesManager.GetActivityByName(activityRecord.Name);
|
||||
|
||||
if (!firstPass && activity.IsBlocking) {
|
||||
return activityRecord;
|
||||
if (!firstPass){
|
||||
if(activity.IsEvent) {
|
||||
activity.Touch(workflowState);
|
||||
blocking.Add(activityRecord);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
firstPass = false;
|
||||
}
|
||||
|
||||
|
||||
var state = FormParametersHelper.FromJsonString(activityRecord.State);
|
||||
var activityContext = new ActivityContext {Tokens = tokens, State = state, WorkflowState = workflowState, Content = target };
|
||||
var outcome = activity.Execute(activityContext);
|
||||
var activityContext = new ActivityContext { Tokens = tokens, State = state, WorkflowState = workflowState, Content = target };
|
||||
var outcomes = activity.Execute(activityContext);
|
||||
|
||||
if (outcome != null) {
|
||||
// look for next activity in the graph
|
||||
var transition = workflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint);
|
||||
if (outcomes != null) {
|
||||
foreach (var outcome in outcomes) {
|
||||
// look for next activity in the graph
|
||||
var transition = workflowDefinitionRecord.TransitionRecords.FirstOrDefault(x => x.SourceActivityRecord == activityRecord && x.SourceEndpoint == outcome.TextHint);
|
||||
|
||||
if (transition == null) {
|
||||
return null;
|
||||
if (transition != null) {
|
||||
pending.Push(transition.DestinationActivityRecord);
|
||||
}
|
||||
}
|
||||
else {
|
||||
activityRecord = transition.DestinationActivityRecord;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// apply Distinct() as two paths could block on the same activity
|
||||
return blocking.Distinct();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,8 @@
|
||||
box-shadow: 2px 2px 19px #aaa;
|
||||
}
|
||||
|
||||
.blocking {
|
||||
.event {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAANNJREFUOE+VkbENhDAMRcOtRElDRRsxB5MwAxNQ0yAGYAJqWiagASFfbMmRHXzcXfGk+Nvf+UocAPzDyxIfMcUnZJF94WYg0ziOkOc50TQNHMehZlQRyLqui4a+74Ok+jrSeZ5TXdc0XJYl7Psu+4Qssm3boKoqMrRtGyRwqHnvo1kZZH4JmxFpcDI/UxQFrOsa2okh5HecH19nGAY6y+1IPKTgbel2RA0xfNsv/0Dwa83zHErdUwWzLIu5HbkJCOa3tiM34bouMljbEVN8whQ/A+4NnH6HdIESjBQAAAAASUVORK5CYII=');
|
||||
/*background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAKVJREFUOE+lkmENwyAQRusJA7WADiSgAA0oqAMUoAAr/QMh38oCbJQbLR3JS5rvjscFugD4CzKcgQxnKB/QWkMphRgjpJRgjJGs64p931uBMaY2bNuGEAI4583GQidwznVN1tqj9hkzcUiXJO0E1EnnpqFgVPzmsaDUfk15KUiM7qkRnJsSWYrzS+V1LUgIIeC9r/9KXu+9dcynkOEMZDgDGd4HywvvUq4US/BOrgAAAABJRU5ErkJggg==');*/
|
||||
background-position: top left;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@@ -130,11 +129,11 @@
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#activity-toolbar > div:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
#activity-toolbar > div:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
#activity-toolbar > div label {
|
||||
#activity-toolbar > div label {
|
||||
display: inline-block;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@@ -146,18 +145,19 @@
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
#activity-toolbar > div label:hover {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
#activity-toolbar > div label:hover {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
/* Start button */
|
||||
#activity-toolbar #activity-toolbar-start-checkbox { /* hide the checkbox and rely on the label for two-state button */
|
||||
display:none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#activity-toolbar #activity-toolbar-start-checkbox + label {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAMCAYAAABfnvydAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAIlJREFUKFOFjrERhBEUhF9PQolIrg4lqEAN+lGBWKoCCWP2/ueOc3MBM5u8/XYXAaDeO4wxEEJMKaVQa30sEJVSoLXe5pKUEjlnUAhhHrz3nJjvvBFXn5WsNcn3O3CduH7yCeyWU9ZatNZoAimlPyDGyGtvYIwB59w2P+kvwDpbVvoHWC1nGgC9AJ139gdKftEFAAAAAElFTkSuQmCC');
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAANCAYAAACdKY9CAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAK1JREFUKFN1ULsNxSAQY6eUNKnSM0dGyERswARMkJqWASIoQMgP8gTigBQuwJ/zHQNAkFJi13XhPE+EECZ++ogxMiEE9n2Hc27iiaikeu+b4Xmed1pvbhW2bfs0VK5UZEop8jFW6gOllGgPrXWeuN7hvu8W2pOvYXWlHIKqmwwrEMNYaQVSaVx6FE9L9x+ccxhjsu4vttbiOA4S+BL1MoVYob9YG11Qu/agu4H9AENqkP+wqtOOAAAAAElFTkSuQmCC');
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
@@ -167,12 +167,13 @@
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Delete button */
|
||||
#activity-toolbar #activity-toolbar-delete > label {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAJCAYAAADkZNYtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAGVJREFUKFOVkLENwCAMBL2TS3p2YwbvwwSuaRnjEywZBRJHSXEFr3sDJgCkqmBmlFLOIywbiIjltVbLqfeOnLOF14KLg5QSWmvrhAgfcLsyEhd5sBf2P4TiU+FVdOabf2/j255BB4ch671zW3IBAAAAAElFTkSuQmCC');
|
||||
background-position: center;
|
||||
}
|
||||
#activity-toolbar #activity-toolbar-start-checkbox:checked + label{
|
||||
|
||||
#activity-toolbar #activity-toolbar-start-checkbox:checked + label {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.branch {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAYCAYAAADpnJ2CAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAJlJREFUSEvtk8sJwCAQBe3JBmzBsqzBfqzAVjx52GhAkM0jagyC4GEum2dm/awgoi6stSSlhBhjUgSv48AiJ8YotNZQllFKUQgBruXAIucIj7AGFhFLx6Km7HZkVzWw+MbeQu89vKMZnHPp10DYeolf4SfxELaOajbXDHD2E/YO9F+5uwv0MVO6K92iTGYkd4RHeDMkXDsWhi7rGDeiuX27SQAAAABJRU5ErkJggg==');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.timer {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAUNJREFUSEu1VsGRhCAQJCeffnz5Nw5DMAJjMAIzMAIj8O3XCPxoWXOMggfY4Fh111W9tYvN9DAMuIqIkhyGgbIsg+z7XkvwPEs4eByHapoGBkWs65q2bYOxHgPTNMEgEo7jqEP48bwfqXJIGZbt/hLLPM9zmudZS3wsy0JlWcI57krOj5jYCJNAiTlJXQZd18UEJ/Z9p6qqqCgKWtfVjP4CJdi2LT9SMPsw8zcDRrgSm+RjY03LmWkXJAa6tSlsbd7wR3lQ3SUGjHAVXCbvQMU6RmoQlpuroXiiHYgFkBpYnRvvk4HVuXTnQIMvJbI6l64BLJFkk6WAmyxpUwmibSo5aBJED5p+9npVvCFxVVwGSMD8s8uOiYSB2EMsKaZJ7Ix7GzD/9YVjGVuJhE5J73hecEvUcil+eukHJMHfFgaaq0nqBz88GIi8S10IAAAAAElFTkSuQmCC');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
string branches = Model.State.Branches;
|
||||
var outcomes = String.Join(",", branches == null ? new string[0] : branches.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => "'" + x.Trim() + "'").ToArray());
|
||||
}
|
||||
|
||||
<div data-outcomes="@outcomes">
|
||||
<div class="branch" ></div>
|
||||
@*@name.CamelFriendly()*@
|
||||
</div>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
string blockingClass = blocking ? "blocking" : null;
|
||||
}
|
||||
|
||||
<div class="@blockingClass">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
}
|
||||
|
||||
<div class="@blocking">
|
||||
<div>
|
||||
<div class="diamond"></div>
|
||||
@*@name.CamelFriendly()*@
|
||||
</div>
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
string blockingClass = blocking ? "blocking" : null;
|
||||
}
|
||||
|
||||
<div class="@blockingClass" title="@Model.State.Message">
|
||||
<div title="@Model.State.Message">
|
||||
@name.CamelFriendly()
|
||||
@if (Model.State.Notification != null) {
|
||||
@:- @Model.State.Notification
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
string title = null;
|
||||
if (Model.State != null && HasText(Model.State.Unity)) {
|
||||
string amount = Model.State.Amount;
|
||||
string unity = Model.State.Unity;
|
||||
title = T("{0} {1} after", amount, T(unity).Text).Text;
|
||||
}
|
||||
}
|
||||
|
||||
<div class="event" title="@title">
|
||||
<div class="timer" ></div>
|
||||
@*@name.CamelFriendly()*@
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
var outcomes = String.Join(",", actions == null ? new string[0] : actions.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries).Select(x => "'" + x.Trim() + "'").ToArray());
|
||||
}
|
||||
|
||||
<div class="blocking" data-outcomes="@outcomes">
|
||||
<div class="event" data-outcomes="@outcomes">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
string blockingClass = blocking ? "blocking" : null;
|
||||
string isEventClass = Model.IsEvent ? "event" : null;
|
||||
string canStartClass = Model.CanStartWorkflow ? "canStart" : null;
|
||||
}
|
||||
|
||||
<div class="@blockingClass">
|
||||
<div class="@isEventClass @canStartClass">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
outcomes: [@Html.Raw(String.Join(",", activity.GetPossibleOutcomes(new ActivityContext()).Where(x => !String.IsNullOrEmpty(x.Text)).Select(x => "'" + HttpUtility.JavaScriptStringEncode(x.Text) + "'").ToArray()))],
|
||||
category: '@HttpUtility.JavaScriptStringEncode(activity.Category.Text)',
|
||||
description: '@HttpUtility.JavaScriptStringEncode(activity.Description.Text)',
|
||||
isBlocking: @(activity.IsBlocking ? "true" : "false"),
|
||||
IsEvent: @(activity.IsEvent ? "true" : "false"),
|
||||
hasForm: @(!String.IsNullOrWhiteSpace(activity.Form) ? "true" : "false")
|
||||
},</text>
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<div id="activity-toolbar">
|
||||
<div id="activity-toolbar-start">
|
||||
<input type="checkbox" id="activity-toolbar-start-checkbox"/>
|
||||
<label for="activity-toolbar-start-checkbox" title="@T("Start")"></label>
|
||||
<label for="activity-toolbar-start-checkbox" title="@T("Starts workflow")"></label>
|
||||
</div>
|
||||
<div id="activity-toolbar-edit">
|
||||
<label title="@T("Edit")"></label>
|
||||
|
||||
Reference in New Issue
Block a user