mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Incremental work on Workflows
--HG-- branch : 1.x extra : rebase_source : 5ce381991d6e285619ad926bf527f6f5db1f425b
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public abstract class ContentActivity : BlockingActivity {
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override bool CanTransition(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);
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
return new[] { T("Success") };
|
||||
}
|
||||
|
||||
public override LocalizedString Transition(ActivityContext context) {
|
||||
return T("True");
|
||||
}
|
||||
}
|
||||
|
||||
public class ContentCreatedActivity : ContentActivity {
|
||||
public override string Name {
|
||||
get { return "ContentCreated"; }
|
||||
}
|
||||
|
||||
public override LocalizedString Category {
|
||||
get { return T("Content Items"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Content is actually created."); }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class DecisionActivity : Activity {
|
||||
|
||||
public DecisionActivity() {
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override string Name {
|
||||
get { return "Decision"; }
|
||||
}
|
||||
|
||||
public override LocalizedString Category {
|
||||
get { return T("Misc"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Evaluates an expression."); }
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
return new[] { T("True"), T("False") };
|
||||
}
|
||||
|
||||
public override LocalizedString Transition(ActivityContext context) {
|
||||
return T("True");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.Activities {
|
||||
public class NotificationActivity : Activity {
|
||||
private readonly INotifier _notifier;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public NotificationActivity(INotifier notifier, ITokenizer tokenizer) {
|
||||
_notifier = notifier;
|
||||
_tokenizer = tokenizer;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override string Name {
|
||||
get { return "Notify"; }
|
||||
}
|
||||
|
||||
public override LocalizedString Category {
|
||||
get { return T("Notification"); }
|
||||
}
|
||||
|
||||
public override LocalizedString Description {
|
||||
get { return T("Display a message."); }
|
||||
}
|
||||
|
||||
public override string Form {
|
||||
get { return "ActivityNotify"; }
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context) {
|
||||
yield return T("Done");
|
||||
}
|
||||
|
||||
public override LocalizedString Transition(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 T("Done");
|
||||
}
|
||||
}
|
||||
}
|
18
src/Orchard.Web/Modules/Orchard.Workflows/AdminMenu.cs
Normal file
18
src/Orchard.Web/Modules/Orchard.Workflows/AdminMenu.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Workflows {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add(T("Workflows"), "10",
|
||||
menu => menu
|
||||
.Add(T("Workflows"), "1.0",
|
||||
qi => qi.Action("Index", "Admin", new { area = "Orchard.Workflows" }).Permission(StandardPermissions.SiteOwner).LocalNav())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,149 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Core.Title.Models;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Core.Contents.Controllers;
|
||||
using Orchard.Data;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Notify;
|
||||
using System;
|
||||
using Orchard.Settings;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Workflows.Models;
|
||||
using Orchard.Workflows.Services;
|
||||
using Orchard.Workflows.ViewModels;
|
||||
|
||||
namespace Orchard.Workflows.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IRepository<WorkflowDefinitionRecord> _workflowDefinitionRecords;
|
||||
private readonly IEnumerable<IActivity> _activities;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices services,
|
||||
IShapeFactory shapeFactory,
|
||||
ISiteService siteService,
|
||||
IRepository<WorkflowDefinitionRecord> workflowDefinitionRecords,
|
||||
IEnumerable<IActivity> activities
|
||||
) {
|
||||
_services = services;
|
||||
_siteService = siteService;
|
||||
_workflowDefinitionRecords = workflowDefinitionRecords;
|
||||
_activities = activities;
|
||||
Services = services;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
dynamic Shape { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index(AdminIndexOptions options, PagerParameters pagerParameters) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list workflows")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
|
||||
// default options
|
||||
if (options == null)
|
||||
options = new AdminIndexOptions();
|
||||
|
||||
var queries = _workflowDefinitionRecords.Table;
|
||||
|
||||
switch (options.Filter) {
|
||||
case WorkflowDefinitionFilter.All:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(options.Search)) {
|
||||
queries = queries.Where(w => w.Name.Contains(options.Search));
|
||||
}
|
||||
|
||||
var pagerShape = Shape.Pager(pager).TotalItemCount(queries.Count());
|
||||
|
||||
switch (options.Order) {
|
||||
case WorkflowDefinitionOrder.Name:
|
||||
queries = queries.OrderBy(u => u.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
var results = queries
|
||||
.Skip(pager.GetStartIndex())
|
||||
.Take(pager.PageSize)
|
||||
.ToList();
|
||||
|
||||
var model = new AdminIndexViewModel {
|
||||
WorkflowDefinitions = results.Select(x => new WorkflowDefinitionEntry {
|
||||
WorkflowDefinitionRecord = x,
|
||||
WokflowDefinitionId = x.Id,
|
||||
Name = x.Name
|
||||
}).ToList(),
|
||||
Options = options,
|
||||
Pager = pagerShape
|
||||
};
|
||||
|
||||
// maintain previous route data when generating page links
|
||||
var routeData = new RouteData();
|
||||
routeData.Values.Add("Options.Filter", options.Filter);
|
||||
routeData.Values.Add("Options.Search", options.Search);
|
||||
routeData.Values.Add("Options.Order", options.Order);
|
||||
|
||||
pagerShape.RouteData(routeData);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public ActionResult Create() {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to create workflows")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Create")]
|
||||
public ActionResult CreatePost(string name) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to create workflows")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var workflowDefinitionRecord = new WorkflowDefinitionRecord {
|
||||
Name = name
|
||||
};
|
||||
|
||||
_workflowDefinitionRecords.Create(workflowDefinitionRecord);
|
||||
|
||||
return RedirectToAction("Edit", new { workflowDefinitionRecord.Id });
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to edit workflows")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var workflowDefinitionRecord = _workflowDefinitionRecords.Get(id);
|
||||
|
||||
var viewModel = new AdminEditViewModel {
|
||||
WorkflowDefinitionRecord = workflowDefinitionRecord,
|
||||
AllActivities = _activities.ToList()
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
public void AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +1,46 @@
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Data.Migration;
|
||||
|
||||
namespace Orchard.Workflows {
|
||||
public class WorkflowsDataMigration : DataMigrationImpl {
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("WorkflowPartRecord",
|
||||
table => table
|
||||
.ContentPartRecord()
|
||||
);
|
||||
public class Migrations : DataMigrationImpl {
|
||||
|
||||
ContentDefinitionManager.AlterTypeDefinition("Workflow",
|
||||
cfg => cfg
|
||||
.WithPart("WorkflowPart")
|
||||
.WithPart("TitlePart")
|
||||
.WithPart("IdentityPart")
|
||||
.WithPart("CommonPart", p => p.WithSetting("OwnerEditorSettings.ShowOwnerEditor", "false"))
|
||||
);
|
||||
public int Create() {
|
||||
// Creating table TransitionRecord
|
||||
SchemaBuilder.CreateTable("TransitionRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<string>("SourceEndpoint")
|
||||
.Column<string>("DestinationEndpoint")
|
||||
.Column<int>("SourceActivityRecord_id")
|
||||
.Column<int>("DestinationActivityRecord_id")
|
||||
.Column<int>("WorkflowDefinitionRecord_id")
|
||||
);
|
||||
|
||||
// Creating table WorkflowRecord
|
||||
SchemaBuilder.CreateTable("WorkflowRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<string>("State", column => column.Unlimited())
|
||||
.Column<int>("WorkflowDefinitionRecord_id")
|
||||
);
|
||||
|
||||
// Creating table WorkflowDefinitionRecord
|
||||
SchemaBuilder.CreateTable("WorkflowDefinitionRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<bool>("Enabled")
|
||||
.Column<string>("Name", column => column.WithLength(1024))
|
||||
);
|
||||
|
||||
// Creating table AwaitingActivityRecord
|
||||
SchemaBuilder.CreateTable("AwaitingActivityRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<int>("ActivityRecord_id")
|
||||
);
|
||||
|
||||
// Creating table ActivityRecord
|
||||
SchemaBuilder.CreateTable("ActivityRecord", table => table
|
||||
.Column<int>("Id", column => column.PrimaryKey().Identity())
|
||||
.Column<string>("Type")
|
||||
.Column<string>("Parameters")
|
||||
.Column<int>("WorkflowDefinitionRecord_id")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -1,16 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Workflows.Models {
|
||||
namespace Orchard.Workflows.Models {
|
||||
/// <summary>
|
||||
/// Represents an activity in a <see cref="WorkflowDefinitionRecord"/>
|
||||
/// </summary>
|
||||
public class ActivityRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the activity.
|
||||
/// </summary>
|
||||
public virtual string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The serialized parameters of the activity.
|
||||
/// </summary>
|
||||
public virtual string Parameters { get; set; }
|
||||
|
||||
// Parent property
|
||||
/// <summary>
|
||||
/// The parent <see cref="WorkflowDefinitionRecord"/>
|
||||
/// containing this activity.
|
||||
/// </summary>
|
||||
public virtual WorkflowDefinitionRecord WorkflowDefinitionRecord { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual IList<ActivityRecord> RegisteredWorkflows { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Workflows.Models {
|
||||
public class AwaitingActivityRecord {
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
public virtual ActivityRecord ActivityRecord { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual IList<WorkflowRecord> WorkflowRecords { get; set; }
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
namespace Orchard.Workflows.Models {
|
||||
public class ConnectionRecord {
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
// Source is null if it's a starting activity
|
||||
public virtual ActivityRecord Source { get; set; }
|
||||
public virtual string SourceEndpoint { get; set; }
|
||||
|
||||
// Destination is null if it's an ending activity
|
||||
public virtual ActivityRecord Destination { get; set; }
|
||||
public virtual string DestinationEndpoint { get; set; }
|
||||
|
||||
// Parent relationship
|
||||
public virtual WorkflowDefinitionRecord WorkflowDefinition { get; set; }
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Models.Descriptors {
|
||||
public class ActionDescriptor {
|
||||
public class ActivityDescriptor {
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public LocalizedString Name { get; set; }
|
||||
|
@@ -6,8 +6,8 @@ namespace Orchard.Workflows.Models.Descriptors {
|
||||
public class DescribeActivityContext {
|
||||
private readonly Dictionary<string, DescribeActivityFor> _describes = new Dictionary<string, DescribeActivityFor>();
|
||||
|
||||
public IEnumerable<TypeDescriptor<ActionDescriptor>> Describe() {
|
||||
return _describes.Select(kp => new TypeDescriptor<ActionDescriptor> {
|
||||
public IEnumerable<TypeDescriptor<ActivityDescriptor>> Describe() {
|
||||
return _describes.Select(kp => new TypeDescriptor<ActivityDescriptor> {
|
||||
Category = kp.Key,
|
||||
Name = kp.Value.Name,
|
||||
Description = kp.Value.Description,
|
||||
|
@@ -7,7 +7,7 @@ namespace Orchard.Workflows.Models.Descriptors {
|
||||
private readonly string _category;
|
||||
|
||||
public DescribeActivityFor(string category, LocalizedString name, LocalizedString description) {
|
||||
Types = new List<ActionDescriptor>();
|
||||
Types = new List<ActivityDescriptor>();
|
||||
_category = category;
|
||||
Name = name;
|
||||
Description = description;
|
||||
@@ -15,10 +15,10 @@ namespace Orchard.Workflows.Models.Descriptors {
|
||||
|
||||
public LocalizedString Name { get; private set; }
|
||||
public LocalizedString Description { get; private set; }
|
||||
public List<ActionDescriptor> Types { get; private set; }
|
||||
public List<ActivityDescriptor> Types { get; private set; }
|
||||
|
||||
public DescribeActivityFor Element(string type, LocalizedString name, LocalizedString description, Func<ActivityContext, bool> action, Func<ActivityContext, LocalizedString> display, string form = null) {
|
||||
Types.Add(new ActionDescriptor { Type = type, Name = name, Description = description, Category = _category, Action = action, Display = display, Form = form });
|
||||
Types.Add(new ActivityDescriptor { Type = type, Name = name, Description = description, Category = _category, Action = action, Display = display, Form = form });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
namespace Orchard.Workflows.Models {
|
||||
/// <summary>
|
||||
/// Reprensents a transition between two <see cref="ActivityRecord"/>
|
||||
/// </summary>
|
||||
public class TransitionRecord {
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source <see cref="ActivityRecord"/>
|
||||
/// </summary>
|
||||
public virtual ActivityRecord SourceActivityRecord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the endpoint on the source <see cref="ActivityRecord"/>
|
||||
/// </summary>
|
||||
public virtual string SourceEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The destination <see cref="ActivityRecord"/>
|
||||
/// </summary>
|
||||
public virtual ActivityRecord DestinationActivityRecord { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the endpoint on the destination <see cref="ActivityRecord"/>
|
||||
/// </summary>
|
||||
public virtual string DestinationEndpoint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent <see cref="WorkflowDefinitionRecord"/>
|
||||
/// </summary>
|
||||
public virtual WorkflowDefinitionRecord WorkflowDefinitionRecord { get; set; }
|
||||
}
|
||||
}
|
@@ -3,22 +3,47 @@ using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Workflows.Models {
|
||||
/// <summary>
|
||||
/// Represent a workflow definition comprised of activities and transitions between them.
|
||||
/// </summary>
|
||||
public class WorkflowDefinitionRecord {
|
||||
public WorkflowDefinitionRecord() {
|
||||
Activities = new List<ActivityRecord>();
|
||||
Connections = new List<ConnectionRecord>();
|
||||
ActivityRecords = new List<ActivityRecord>();
|
||||
TransitionRecords = new List<TransitionRecord>();
|
||||
}
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to enable workflows of this type.
|
||||
/// </summary>
|
||||
public virtual bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the workflow definition.
|
||||
/// </summary>
|
||||
[Required, StringLength(1024)]
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="ActivityRecord"/> composing this workflow definition.
|
||||
/// </summary>
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ActivityRecord> Activities { get; set; }
|
||||
public virtual IList<ActivityRecord> ActivityRecords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="TransitionRecord"/> composing this workflow definition.
|
||||
/// This is distinct from Activities as there could be activities without
|
||||
/// any connection an any time of the design process, though they should
|
||||
/// be synchronized.
|
||||
/// </summary>
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ConnectionRecord> Connections { get; set; }
|
||||
public virtual IList<TransitionRecord> TransitionRecords { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of <see cref="WorkflowRecord"/> associated with this workflow definition.
|
||||
/// </summary>
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<WorkflowRecord> WorkflowRecords { get; set; }
|
||||
}
|
||||
}
|
@@ -2,16 +2,33 @@
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Workflows.Models {
|
||||
/// <summary>
|
||||
/// Reprensents a running workflow instance.
|
||||
/// </summary>
|
||||
public class WorkflowRecord {
|
||||
public WorkflowRecord() {
|
||||
AwaitingActivities = new List<ActivityRecord>();
|
||||
AwaitingActivities = new List<AwaitingActivityRecord>();
|
||||
}
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serialized state of the workflow.
|
||||
/// </summary>
|
||||
[StringLengthMax]
|
||||
public virtual string State { get; set; }
|
||||
|
||||
public virtual IList<ActivityRecord> AwaitingActivities { get; set; }
|
||||
/// <summary>
|
||||
/// List of activities the current workflow instance is waiting on
|
||||
/// for continuing its process.
|
||||
/// </summary>
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<AwaitingActivityRecord> AwaitingActivities { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent <see cref="WorkflowDefinitionRecord"/>
|
||||
/// </summary>
|
||||
public virtual WorkflowDefinitionRecord WorkflowDefinitionRecord { get; set; }
|
||||
|
||||
}
|
||||
}
|
@@ -7,4 +7,5 @@ OrchardVersion: 1.0
|
||||
Description: Description for the module
|
||||
Features:
|
||||
Orchard.Workflows:
|
||||
Description: Description for feature Orchard.Workflows.
|
||||
Description: Description for feature Orchard.Workflows.
|
||||
Dependencies: Orchard.Tokens, Orchard.Forms, Orchard.jQuery
|
@@ -64,6 +64,9 @@
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\jquery.jsPlumb-1.3.16-all-min.js" />
|
||||
<Content Include="Styles\admin-workflows.css" />
|
||||
<Content Include="Styles\images\blocking.png" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Scripts\Web.config" />
|
||||
@@ -80,23 +83,63 @@
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
|
||||
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
|
||||
<Name>Orchard.Forms</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Tokens\Orchard.Tokens.csproj">
|
||||
<Project>{6f759635-13d7-4e94-bcc9-80445d63f117}</Project>
|
||||
<Name>Orchard.Tokens</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\ContentActivity.cs" />
|
||||
<Compile Include="Activities\DecisionActivity.cs" />
|
||||
<Compile Include="Activities\NotificationActivity.cs" />
|
||||
<Compile Include="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Models\AwaitingActivityRecord.cs" />
|
||||
<Compile Include="Models\Descriptors\ActivityContext.cs" />
|
||||
<Compile Include="Models\Descriptors\ActivityDescriptor.cs" />
|
||||
<Compile Include="Models\Descriptors\DescribeActivityContext.cs" />
|
||||
<Compile Include="Models\Descriptors\DescribeActivityFor.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\ActivityRecord.cs" />
|
||||
<Compile Include="Models\ConnectionRecord.cs" />
|
||||
<Compile Include="Models\TransitionRecord.cs" />
|
||||
<Compile Include="Models\Descriptors\TypeDescriptor.cs" />
|
||||
<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\NotificationActivityForms.cs" />
|
||||
<Compile Include="Providers\NotificationActivityProvider.cs" />
|
||||
<Compile Include="Services\Activity.cs" />
|
||||
<Compile Include="Services\BlockingActivity.cs" />
|
||||
<Compile Include="Services\IActivity.cs" />
|
||||
<Compile Include="Services\IActivityProvider.cs" />
|
||||
<Compile Include="Services\ActivitiesManager.cs" />
|
||||
<Compile Include="Services\IActivitiesManager.cs" />
|
||||
<Compile Include="ViewModels\AdminEditViewModel.cs" />
|
||||
<Compile Include="ViewModels\AdminIndexViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Controllers\" />
|
||||
<Folder Include="Handlers\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Create.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity-ContentCreated.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Activity.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
public class ContentForms : IFormProvider {
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ContentForms(
|
||||
IShapeFactory shapeFactory,
|
||||
IContentDefinitionManager contentDefinitionManager) {
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, dynamic> form =
|
||||
shape => {
|
||||
|
||||
var f = Shape.Form(
|
||||
Id: "AnyOfContentTypes",
|
||||
_Parts: Shape.SelectList(
|
||||
Id: "contenttypes", Name: "contenttypes",
|
||||
Title: T("Content types"),
|
||||
Description: T("Select some content types."),
|
||||
Size: 10,
|
||||
Multiple: true
|
||||
)
|
||||
);
|
||||
|
||||
f._Parts.Add(new SelectListItem { Value = "", Text = T("Any").Text });
|
||||
|
||||
foreach (var contentType in _contentDefinitionManager.ListTypeDefinitions().OrderBy(x => x.DisplayName)) {
|
||||
f._Parts.Add(new SelectListItem { Value = contentType.Name, Text = contentType.DisplayName });
|
||||
}
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form("SelectContentTypes", form);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Workflows.Providers {
|
||||
public class NotificationActivityForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public NotificationActivityForms(IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
context.Form("ActivityNotify",
|
||||
shape => Shape.Form(
|
||||
Id: "ActivityNotify",
|
||||
_Type: Shape.SelectList(
|
||||
Id: "notification", Name: "notification",
|
||||
Title: T("Notification type"),
|
||||
Description: T("Select what type of notification should be displayed."))
|
||||
.Add(new SelectListItem { Value = "Information", Text = T("Information").Text })
|
||||
.Add(new SelectListItem { Value = "Warning", Text = T("Warning").Text })
|
||||
.Add(new SelectListItem { Value = "Error", Text = T("Error").Text }),
|
||||
_Message: Shape.Textbox(
|
||||
Id: "message", Name: "message",
|
||||
Title: T("Message"),
|
||||
Description: T("The notification message to display."),
|
||||
Classes: new[] { "textMedium", "tokenized" })
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
1
src/Orchard.Web/Modules/Orchard.Workflows/Scripts/jquery.jsPlumb-1.3.16-all-min.js
vendored
Normal file
1
src/Orchard.Web/Modules/Orchard.Workflows/Scripts/jquery.jsPlumb-1.3.16-all-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Caching;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public class ActivitiesManager : IActivitiesManager{
|
||||
private const string SignalName = "Orchard.Workflows.Services.ActivitiesManager";
|
||||
|
||||
private readonly ITokenizer _tokenizer;
|
||||
private readonly IEnumerable<IActivityProvider> _activityProviders;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly ISignals _signals;
|
||||
|
||||
public ActivitiesManager(
|
||||
ITokenizer tokenizer,
|
||||
IEnumerable<IActivityProvider> activityProviders,
|
||||
IContentManager contentManager,
|
||||
ICacheManager cacheManager,
|
||||
ISignals signals) {
|
||||
_tokenizer = tokenizer;
|
||||
_activityProviders = activityProviders;
|
||||
_contentManager = contentManager;
|
||||
_cacheManager = cacheManager;
|
||||
_signals = signals;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public IEnumerable<TypeDescriptor<ActivityDescriptor>> DescribeActivities() {
|
||||
return _cacheManager.Get("activities", ctx => {
|
||||
MonitorSignal(ctx);
|
||||
|
||||
var context = new DescribeActivityContext();
|
||||
|
||||
foreach (var provider in _activityProviders) {
|
||||
provider.Describe(context);
|
||||
}
|
||||
return context.Describe();
|
||||
});
|
||||
}
|
||||
|
||||
private void MonitorSignal(AcquireContext<string> ctx) {
|
||||
ctx.Monitor(_signals.When(SignalName));
|
||||
}
|
||||
|
||||
private void TriggerSignal() {
|
||||
_signals.Trigger(SignalName);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public abstract class Activity : IActivity {
|
||||
|
||||
public abstract string Name { get; }
|
||||
public abstract LocalizedString Category { get; }
|
||||
public abstract LocalizedString Description { get; }
|
||||
|
||||
public virtual bool IsBlocking {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public virtual string Form {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
|
||||
|
||||
public virtual bool CanTransition(ActivityContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract LocalizedString Transition(ActivityContext context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public abstract class BlockingActivity : IActivity {
|
||||
|
||||
public abstract string Name { get; }
|
||||
public abstract LocalizedString Category { get; }
|
||||
public abstract LocalizedString Description { get; }
|
||||
|
||||
public virtual bool IsBlocking {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual string Form {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public abstract IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
|
||||
|
||||
public virtual bool CanTransition(ActivityContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract LocalizedString Transition(ActivityContext context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public interface IActivitiesManager : IDependency {
|
||||
IEnumerable<TypeDescriptor<ActivityDescriptor>> DescribeActivities();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public interface IActivity : IDependency {
|
||||
|
||||
string Name { get; }
|
||||
LocalizedString Category { get; }
|
||||
LocalizedString Description { get; }
|
||||
bool IsBlocking { get; }
|
||||
string Form { get; }
|
||||
|
||||
IEnumerable<LocalizedString> GetPossibleOutcomes(ActivityContext context);
|
||||
bool CanTransition(ActivityContext context);
|
||||
LocalizedString Transition(ActivityContext context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
using Orchard.Events;
|
||||
using Orchard.Workflows.Models.Descriptors;
|
||||
|
||||
namespace Orchard.Workflows.Services {
|
||||
public interface IActivityProvider : IEventHandler {
|
||||
void Describe(DescribeActivityContext describe);
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
.activity {
|
||||
border: 1px solid #346789;
|
||||
/*box-shadow: 2px 2px 19px #aaa;
|
||||
-o-box-shadow: 2px 2px 19px #aaa;
|
||||
-webkit-box-shadow: 2px 2px 19px #aaa;
|
||||
-moz-box-shadow: 2px 2px 19px #aaa;
|
||||
-moz-border-radius: 0.5em;*/
|
||||
border-radius: 0.5em;
|
||||
opacity: 0.8;
|
||||
filter: alpha(opacity=80);
|
||||
min-width: 5em;
|
||||
min-height: 5em;
|
||||
line-height: 5em;
|
||||
text-align: center;
|
||||
z-index: 20;
|
||||
position: absolute;
|
||||
background-color: #eeeeef;
|
||||
color: black;
|
||||
font-family: helvetica;
|
||||
padding: 0.5em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.activity:hover, .dragHover {
|
||||
box-shadow: 2px 2px 19px #444;
|
||||
-o-box-shadow: 2px 2px 19px #444;
|
||||
-webkit-box-shadow: 2px 2px 19px #444;
|
||||
-moz-box-shadow: 2px 2px 19px #444;
|
||||
opacity: 0.6;
|
||||
filter: alpha(opacity=60);
|
||||
}
|
||||
|
||||
.blocking {
|
||||
background-image: url(images/blocking.png) ;
|
||||
background-position: top right;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
._jsPlumb_connector { z-index:4; }
|
||||
._jsPlumb_endpoint { z-index:21;cursor:pointer; }
|
||||
|
||||
.sourceEndpointLabel, .targetEndpointLabel {
|
||||
z-index: 21;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.active {
|
||||
border:1px dotted green;
|
||||
}
|
||||
.hover {
|
||||
border:1px dotted red;
|
||||
}
|
||||
|
||||
|
||||
/* attempt to make the editor section to take the full height */
|
||||
#layout-main, #main, #content {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.editor {
|
||||
position: relative;
|
||||
height:auto !important; /* real browsers */
|
||||
min-height:500px; /* real browsers */
|
||||
}
|
||||
|
||||
.connection-label {
|
||||
z-index: 10;
|
||||
opacity:0.8;
|
||||
filter:alpha(opacity=80);
|
||||
background-color:white;
|
||||
color:black;
|
||||
padding:0 0.5em;
|
||||
border:1px solid #346789;
|
||||
}
|
||||
|
||||
/* toolbox */
|
||||
|
||||
#activity-toolbox {
|
||||
border: 1px solid #E4E5E6;
|
||||
background-color: #F3F4F5;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
#activity-toolbox .activity-toolbox-item {
|
||||
display: block;
|
||||
padding: 0 10px;
|
||||
width: 150px;
|
||||
border: 1px solid #EAEAEA;
|
||||
background-color: white;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#activity-toolbox .activity-toolbox-item h2 {
|
||||
color: #333;
|
||||
padding: 2px;
|
||||
font-size: 1.077em;
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 653 B |
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Workflows.Models;
|
||||
using Orchard.Workflows.Services;
|
||||
|
||||
namespace Orchard.Workflows.ViewModels {
|
||||
public class AdminEditViewModel {
|
||||
public WorkflowDefinitionRecord WorkflowDefinitionRecord { get; set; }
|
||||
public IEnumerable<IActivity> AllActivities { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Workflows.Models;
|
||||
|
||||
namespace Orchard.Workflows.ViewModels {
|
||||
|
||||
public class AdminIndexViewModel {
|
||||
public IList<WorkflowDefinitionEntry> WorkflowDefinitions { get; set; }
|
||||
public AdminIndexOptions Options { get; set; }
|
||||
public dynamic Pager { get; set; }
|
||||
}
|
||||
|
||||
public class WorkflowDefinitionEntry {
|
||||
public WorkflowDefinitionRecord WorkflowDefinitionRecord { get; set; }
|
||||
public bool IsChecked { get; set; }
|
||||
|
||||
public int WokflowDefinitionId { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class AdminIndexOptions {
|
||||
public string Search { get; set; }
|
||||
public WorkflowDefinitionOrder Order { get; set; }
|
||||
public WorkflowDefinitionFilter Filter { get; set; }
|
||||
public WorkflowDefinitionBulk BulkAction { get; set; }
|
||||
}
|
||||
|
||||
public enum WorkflowDefinitionOrder {
|
||||
Name,
|
||||
Creation
|
||||
}
|
||||
|
||||
public enum WorkflowDefinitionFilter {
|
||||
All
|
||||
}
|
||||
|
||||
public enum WorkflowDefinitionBulk {
|
||||
None,
|
||||
Delete
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
string blockingClass = blocking ? "blocking" : null;
|
||||
}
|
||||
|
||||
<div class="activity @blockingClass" style="background-color: pink">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
@@ -0,0 +1,11 @@
|
||||
@using Orchard.Utility.Extensions
|
||||
|
||||
@{
|
||||
string name = Model.Name;
|
||||
bool blocking = Model.IsBlocking;
|
||||
}
|
||||
|
||||
<div class="activity @blocking">
|
||||
@name.CamelFriendly()
|
||||
</div>
|
||||
|
@@ -0,0 +1,19 @@
|
||||
@model Orchard.Workflows.ViewModels.AdminEditViewModel
|
||||
@using Orchard.Workflows.Models;
|
||||
@using Orchard.Workflows.ViewModels;
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
<div>
|
||||
<label for="name">@T("Name")</label>
|
||||
@Html.TextBox("name", String.Empty, new { @class= "textMedium text" })
|
||||
<span class="hint">@T("The name of the Workflow")</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||
</fieldset>
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
@model Orchard.Workflows.ViewModels.AdminEditViewModel
|
||||
@using ClaySharp.Implementation
|
||||
@using Orchard.ContentManagement
|
||||
@using Orchard.Localization
|
||||
@using Orchard.Utility.Extensions
|
||||
@using Orchard.Workflows.Models;
|
||||
@using Orchard.Workflows.ViewModels;
|
||||
@using Orchard.DisplayManagement;
|
||||
|
||||
@{
|
||||
Layout.Title = @T("Edit Workflow");
|
||||
Style.Include("admin-workflows.css");
|
||||
Script.Require("jQueryUI");
|
||||
Script.Include("jquery.jsPlumb-1.3.16-all-min.js");
|
||||
|
||||
// var editorShape = ((IShapeFactory)New).Create(activity.Name + "_Editor");
|
||||
|
||||
}
|
||||
|
||||
<!-- List of available activities -->
|
||||
<div id="activity-toolbox">
|
||||
<ul>
|
||||
@foreach (var activity in Model.AllActivities) {
|
||||
IShape shape = New.Activity(activity);
|
||||
shape.Metadata.Alternates.Add("Activity__" + activity.Name);
|
||||
|
||||
<li class="activity-toolbox-item" data-activity-name="@activity.Name">
|
||||
<h2>@activity.Name</h2>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<div class="editor">
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Render script to initialize a new jsplumb shapes in the form of activities['activity-name'].create() *@
|
||||
@using (Script.Foot()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var activities = {};
|
||||
@foreach (var activity in Model.AllActivities) {
|
||||
IShape shape = New.Activity(activity);
|
||||
shape.Metadata.Alternates.Add("Activity__" + activity.Name);
|
||||
<text>
|
||||
activities['@activity.Name'] = {};
|
||||
activities['@activity.Name']['create'] = function () {
|
||||
var dom = $('@Html.Raw(HttpUtility.JavaScriptStringEncode(Display(shape).ToString()))');
|
||||
var editor = $('.editor');
|
||||
editor.append(dom);
|
||||
jsPlumb.draggable(dom, { containment: "parent", scroll: true });
|
||||
|
||||
jsPlumb.makeTarget(dom, {
|
||||
dropOptions: { hoverClass: "dragHover" },
|
||||
anchor: "Continuous",
|
||||
endpoint: "Blank",
|
||||
paintStyle: { fillStyle: "#558822", radius: 3 },
|
||||
});
|
||||
</text>
|
||||
|
||||
var outcomes = activity.GetPossibleOutcomes(null);
|
||||
foreach(var outcome in outcomes) {
|
||||
<text>
|
||||
jsPlumb.addEndpoint(dom, {
|
||||
anchor: "Continuous",
|
||||
connectorOverlays: [["Label", { label: "@HttpUtility.JavaScriptStringEncode(outcome.ToString())", cssClass: "connection-label" }]],
|
||||
}, sourceEndpointOptions);
|
||||
</text>
|
||||
}
|
||||
|
||||
<text>
|
||||
};
|
||||
</text>
|
||||
}
|
||||
//]]>
|
||||
</script>
|
||||
}
|
||||
|
||||
@using (Script.Foot()) {
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var connectorPaintStyle = {
|
||||
lineWidth: 3,
|
||||
strokeStyle: "grey",
|
||||
joinstyle: "round",
|
||||
//outlineColor: "white",
|
||||
//outlineWidth: 7
|
||||
};
|
||||
|
||||
var connectorHoverStyle = {
|
||||
lineWidth: 3,
|
||||
strokeStyle: "#2e2aF8"
|
||||
};
|
||||
|
||||
var sourceEndpointOptions = {
|
||||
endpoint: "Dot",
|
||||
paintStyle: { fillStyle: "#225588", radius: 7 },
|
||||
isSource: true,
|
||||
isTarget: false,
|
||||
connector: ["Flowchart"], // gap needs to be the same as makeTarget.paintStyle.radius
|
||||
connectorStyle: connectorPaintStyle,
|
||||
hoverPaintStyle: connectorHoverStyle,
|
||||
connectorHoverStyle: connectorHoverStyle,
|
||||
overlays: [["Label", { location: [0.5, 1.5], cssClass: "sourceEndpointLabel" }]]
|
||||
};
|
||||
|
||||
jsPlumb.bind("ready", function() {
|
||||
|
||||
jsPlumb.importDefaults({
|
||||
// default drag options
|
||||
DragOptions : { cursor: 'pointer', zIndex:2000 },
|
||||
// default to blue at one end and green at the other
|
||||
EndpointStyles : [{ fillStyle:'#225588' }, { fillStyle:'#558822' }],
|
||||
// blue endpoints 7 px; green endpoints 11.
|
||||
Endpoints : [ [ "Dot", {radius:7} ], [ "Dot", { radius:7 } ]],
|
||||
// the overlays to decorate each connection with. note that the label overlay uses a function to generate the label text; in this
|
||||
// case it returns the 'labelText' member that we set on each connection in the 'init' method below.
|
||||
ConnectionOverlays: [
|
||||
["Arrow", { width: 12, length: 12, location: 1 }],
|
||||
// ["Label", { location: 0.1, id: "label", cssClass: "aLabel" }]
|
||||
],
|
||||
ConnectorZIndex:5
|
||||
});
|
||||
|
||||
//jsPlumb.bind("jsPlumbConnection", function (connInfo, originalEvent) {
|
||||
// init(connInfo.connection);
|
||||
//});
|
||||
});
|
||||
|
||||
// a new connection is created
|
||||
jsPlumb.bind("jsPlumbConnection", function(connectionInfo) {
|
||||
// ...update your data model here. The contents of the 'connectionInfo' are described below.
|
||||
});
|
||||
|
||||
// a connection is detached
|
||||
jsPlumb.bind("jsPlumbConnectionDetached", function(connectionInfo) {
|
||||
// ...update your data model here. The contents of the 'connectionInfo' are described below.
|
||||
});
|
||||
|
||||
// create a new activity node on the editor
|
||||
$('.activity-toolbox-item').on('click', function () {
|
||||
var self = $(this);
|
||||
var activityName = self.data('activity-name');
|
||||
activities[activityName].create();
|
||||
});
|
||||
//]]>
|
||||
</script>
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
@model Orchard.Workflows.ViewModels.AdminIndexViewModel
|
||||
@using Orchard.Workflows.Models;
|
||||
@using Orchard.Workflows.ViewModels;
|
||||
|
||||
@{
|
||||
var index = 0;
|
||||
|
||||
var pageSizes = new List<int?>() { 10, 50, 100 };
|
||||
var defaultPageSize = WorkContext.CurrentSite.PageSize;
|
||||
if(!pageSizes.Contains(defaultPageSize)) {
|
||||
pageSizes.Add(defaultPageSize);
|
||||
}
|
||||
}
|
||||
|
||||
<h1>@Html.TitleForPage(T("Manage Workflows").ToString()) </h1>
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
<div class="manage">@Html.ActionLink(T("Create new Wokflow").ToString(), "Create", new { Area = "Orchard.Workflows", returnurl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })</div>
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="publishActions">@T("Actions:")</label>
|
||||
<select id="publishActions" name="@Html.NameOf(m => m.Options.BulkAction)">
|
||||
@Html.SelectOption(Model.Options.BulkAction, WorkflowDefinitionBulk.None, T("Choose action...").ToString())
|
||||
@Html.SelectOption(Model.Options.BulkAction, WorkflowDefinitionBulk.Delete, T("Delete").ToString())
|
||||
</select>
|
||||
<button type="submit" name="submit.BulkEdit" value="@T("Apply")">@T("Apply")</button>
|
||||
</fieldset>
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="filterResults">@T("Sort by:")</label>
|
||||
<select id="filterResults" name="@Html.NameOf(m => m.Options.Order)">
|
||||
@Html.SelectOption(Model.Options.Order, WorkflowDefinitionOrder.Name, T("Name").ToString())
|
||||
</select>
|
||||
<input type="hidden" name="Page" value="1" />
|
||||
<label for="pageSize">@T("Show:")</label>
|
||||
<select id="pageSize" name="PageSize">
|
||||
@Html.SelectOption((int)Model.Pager.PageSize, 0, T("All").ToString())
|
||||
@foreach(int size in pageSizes.OrderBy(p => p)) {
|
||||
@Html.SelectOption((int)Model.Pager.PageSize, size, size.ToString())
|
||||
}
|
||||
</select>
|
||||
<button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="checkbox"> ↓</th>
|
||||
<th scope="col">@T("Name")</th>
|
||||
<th scope="col" class="actions"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var entry in Model.WorkflowDefinitions) {
|
||||
<tr>
|
||||
<td>
|
||||
<input type="hidden" value="@Model.WorkflowDefinitions[index].WokflowDefinitionId" name="@Html.NameOf(m => m.WorkflowDefinitions[index].WokflowDefinitionId)"/>
|
||||
<input type="checkbox" value="true" name="@Html.NameOf(m => m.WorkflowDefinitions[index].IsChecked)"/>
|
||||
</td>
|
||||
<td>
|
||||
@Html.ActionLink(entry.Name, "Edit", new { id = entry.WokflowDefinitionId })
|
||||
</td>
|
||||
<td>
|
||||
@*@Html.ActionLink(T("Properties").ToString(), "Edit", new { Area = "Contents", id = entry.QueryId, returnurl = HttpContext.Current.Request.RawUrl })*@ |
|
||||
@Html.ActionLink(T("Edit").ToString(), "Edit", new { id = entry.WokflowDefinitionId}) |
|
||||
@*@Html.ActionLink(T("Delete").ToString(), "Delete", new { id = entry.QueryId }, new { itemprop = "RemoveUrl UnsafeUrl" })*@
|
||||
</td>
|
||||
</tr>
|
||||
index++;
|
||||
}
|
||||
</table>
|
||||
@Display(Model.Pager)
|
||||
</fieldset>
|
||||
}
|
Reference in New Issue
Block a user