mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-01-24 05:42:10 +08:00
Including Orchard.Rule
--HG-- branch : 1.x
This commit is contained in:
18
src/Orchard.Web/Modules/Orchard.Rules/AdminMenu.cs
Normal file
18
src/Orchard.Web/Modules/Orchard.Rules/AdminMenu.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Orchard.Localization;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Navigation;
|
||||
|
||||
namespace Orchard.Rules {
|
||||
public class AdminMenu : INavigationProvider {
|
||||
public Localizer T { get; set; }
|
||||
public string MenuName { get { return "admin"; } }
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add(T("Rules"), "4",
|
||||
menu => menu
|
||||
.Add(T("Manage Rules"), "1.0",
|
||||
item => item.Action("Index", "Admin", new { area = "Orchard.Rules" }).Permission(StandardPermissions.SiteOwner))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 293 B |
Binary file not shown.
|
After Width: | Height: | Size: 405 B |
25
src/Orchard.Web/Modules/Orchard.Rules/Content/Web.config
Normal file
25
src/Orchard.Web/Modules/Orchard.Rules/Content/Web.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -0,0 +1,169 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Rules.ViewModels;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Rules.Controllers {
|
||||
[ValidateInput(false), Admin]
|
||||
public class ActionController : Controller {
|
||||
public ActionController(
|
||||
IOrchardServices services,
|
||||
IRulesManager rulesManager,
|
||||
IRulesServices rulesServices,
|
||||
IFormManager formManager,
|
||||
IShapeFactory shapeFactory) {
|
||||
Services = services;
|
||||
_rulesManager = rulesManager;
|
||||
_rulesServices = rulesServices;
|
||||
_formManager = formManager;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; set; }
|
||||
private readonly IRulesManager _rulesManager;
|
||||
private readonly IRulesServices _rulesServices;
|
||||
private readonly IFormManager _formManager;
|
||||
public Localizer T { get; set; }
|
||||
public dynamic Shape { get; set; }
|
||||
|
||||
public ActionResult Add(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var viewModel = new AddActionViewModel { Id = id, Actions = _rulesManager.DescribeActions() };
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id, int actionId) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
_rulesServices.DeleteAction(actionId);
|
||||
Services.Notifier.Information(T("Action Deleted"));
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id, string category, string type, int actionId = -1) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var action = _rulesManager.DescribeActions().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == category && x.Type == type);
|
||||
|
||||
if (action == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
// if there is no form to edit, save the action and go back to the rule
|
||||
if (action.Form == null) {
|
||||
if (actionId == -1) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
rule.Actions.Add(new ActionRecord { Category = category, Type = type, Position = rule.Actions.Count + 1 });
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
// build the form, and let external components alter it
|
||||
var form = _formManager.Build(action.Form);
|
||||
|
||||
// generate an anti forgery token
|
||||
var viewContext = new ViewContext { HttpContext = HttpContext, Controller = this };
|
||||
var token = new HtmlHelper(viewContext, new ViewDataContainer()).AntiForgeryToken();
|
||||
|
||||
// add a submit button to the form
|
||||
form
|
||||
._Actions(Shape.Fieldset(
|
||||
_RequestAntiForgeryToken: Shape.Markup(
|
||||
Value: token.ToString()),
|
||||
_Save: Shape.Submit(
|
||||
Name: "op",
|
||||
Value: T("Save"))
|
||||
)
|
||||
);
|
||||
|
||||
// bind form with existing values).
|
||||
if (actionId != -1) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
var actionRecord = rule.Actions.FirstOrDefault(a => a.Id == actionId);
|
||||
if (actionRecord != null) {
|
||||
var parameters = FormParametersHelper.FromString(actionRecord.Parameters);
|
||||
_formManager.Bind(form, new DictionaryValueProvider<string>(parameters, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
var viewModel = new EditActionViewModel { Id = id, Action = action, Form = form };
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
public ActionResult EditPost(int id, string category, string type, [DefaultValue(-1)]int actionId, FormCollection formCollection) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
|
||||
var actionRecord = rule.Actions.FirstOrDefault(a => a.Id == actionId);
|
||||
|
||||
// add new action record if it's a newly created action
|
||||
if (actionRecord == null) {
|
||||
actionRecord = new ActionRecord { Category = category, Type = type, Position = rule.Actions.Count };
|
||||
rule.Actions.Add(actionRecord);
|
||||
}
|
||||
|
||||
var action = _rulesManager.DescribeActions().SelectMany(x => x.Descriptors).FirstOrDefault(x => x.Category == category && x.Type == type);
|
||||
|
||||
// validating form values
|
||||
_formManager.Validate(new ValidatingContext { FormName = action.Form, ModelState = ModelState, ValueProvider = ValueProvider });
|
||||
|
||||
if (ModelState.IsValid) {
|
||||
var dictionary = formCollection.AllKeys.ToDictionary(key => key, formCollection.Get);
|
||||
|
||||
// save form parameters
|
||||
actionRecord.Parameters = FormParametersHelper.ToString(dictionary);
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
// model is invalid, display it again
|
||||
var form = _formManager.Build(action.Form);
|
||||
|
||||
// Cancel the current transaction to prevent records from begin created
|
||||
Services.TransactionManager.Cancel();
|
||||
|
||||
AddSubmitButton(form);
|
||||
|
||||
_formManager.Bind(form, formCollection);
|
||||
var viewModel = new EditActionViewModel { Id = id, Action = action, Form = form };
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
private class ViewDataContainer : IViewDataContainer {
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
}
|
||||
|
||||
private void AddSubmitButton(dynamic form) {
|
||||
var viewContext = new ViewContext { HttpContext = HttpContext, Controller = this };
|
||||
var token = new HtmlHelper(viewContext, new ViewDataContainer()).AntiForgeryToken();
|
||||
|
||||
// add a submit button to the form
|
||||
form
|
||||
._Actions(Shape.Fieldset(
|
||||
_RequestAntiForgeryToken: Shape.Markup(
|
||||
Value: token.ToString()),
|
||||
_Save: Shape.Submit(
|
||||
Name: "op",
|
||||
Value: T("Save"))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Rules.ViewModels;
|
||||
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;
|
||||
|
||||
namespace Orchard.Rules.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly ISiteService _siteService;
|
||||
private readonly IRulesManager _rulesManager;
|
||||
private readonly IRulesServices _rulesServices;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices services,
|
||||
IShapeFactory shapeFactory,
|
||||
ISiteService siteService,
|
||||
IRulesManager rulesManager,
|
||||
IRulesServices rulesServices,
|
||||
IRepository<RuleRecord> repository) {
|
||||
_siteService = siteService;
|
||||
_rulesManager = rulesManager;
|
||||
_rulesServices = rulesServices;
|
||||
Services = services;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
dynamic Shape { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index(RulesIndexOptions options, PagerParameters pagerParameters) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
|
||||
|
||||
// default options
|
||||
if (options == null)
|
||||
options = new RulesIndexOptions();
|
||||
|
||||
var rules = _rulesServices.GetRules();
|
||||
|
||||
switch (options.Filter) {
|
||||
case RulesFilter.Disabled:
|
||||
rules = rules.Where(r => r.Enabled == false);
|
||||
break;
|
||||
case RulesFilter.Enabled:
|
||||
rules = rules.Where(u => u.Enabled);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(options.Search)) {
|
||||
rules = rules.Where(r => r.Name.Contains(options.Search));
|
||||
}
|
||||
|
||||
var pagerShape = Shape.Pager(pager).TotalItemCount(rules.Count());
|
||||
|
||||
switch (options.Order) {
|
||||
case RulesOrder.Name:
|
||||
rules = rules.OrderBy(u => u.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
var results = rules
|
||||
.Skip(pager.GetStartIndex())
|
||||
.Take(pager.PageSize)
|
||||
.ToList();
|
||||
|
||||
var model = new RulesIndexViewModel {
|
||||
Rules = results.Select(x => new RulesEntry {
|
||||
Rule = x,
|
||||
IsChecked = false,
|
||||
RuleId = x.Id
|
||||
}).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);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[FormValueRequired("submit.BulkEdit")]
|
||||
public ActionResult Index(FormCollection input) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var viewModel = new RulesIndexViewModel { Rules = new List<RulesEntry>(), Options = new RulesIndexOptions() };
|
||||
UpdateModel(viewModel);
|
||||
|
||||
var checkedEntries = viewModel.Rules.Where(c => c.IsChecked);
|
||||
switch (viewModel.Options.BulkAction) {
|
||||
case RulesBulkAction.None:
|
||||
break;
|
||||
case RulesBulkAction.Enable:
|
||||
foreach (var entry in checkedEntries) {
|
||||
_rulesServices.GetRule(entry.RuleId).Enabled = true;
|
||||
}
|
||||
break;
|
||||
case RulesBulkAction.Disable:
|
||||
foreach (var entry in checkedEntries) {
|
||||
_rulesServices.GetRule(entry.RuleId).Enabled = false;
|
||||
}
|
||||
break;
|
||||
case RulesBulkAction.Delete:
|
||||
foreach (var entry in checkedEntries) {
|
||||
_rulesServices.DeleteRule(entry.RuleId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult Move(string direction, int id, int actionId) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
switch(direction) {
|
||||
case "up" : _rulesServices.MoveUp(actionId);
|
||||
break;
|
||||
case "down": _rulesServices.MoveDown(actionId);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("direction");
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
public ActionResult Create() {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
return View(new CreateRuleViewModel());
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Create")]
|
||||
public ActionResult CreatePost(CreateRuleViewModel viewModel) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
}
|
||||
else {
|
||||
var rule = _rulesServices.CreateRule(viewModel.Name);
|
||||
return RedirectToAction("Edit", new { id = rule.Id });
|
||||
}
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to edit rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
var viewModel = new EditRuleViewModel {
|
||||
Id = rule.Id,
|
||||
Enabled = rule.Enabled,
|
||||
Name = rule.Name
|
||||
};
|
||||
|
||||
#region Load events
|
||||
var eventEntries = new List<EventEntry>();
|
||||
var allEvents = _rulesManager.DescribeEvents().SelectMany(x => x.Descriptors);
|
||||
|
||||
foreach (var eventRecord in rule.Events) {
|
||||
var category = eventRecord.Category;
|
||||
var type = eventRecord.Type;
|
||||
|
||||
var ev = allEvents.Where(x => category == x.Category && type == x.Type).FirstOrDefault();
|
||||
if (ev != null) {
|
||||
var eventParameters = FormParametersHelper.FromString(eventRecord.Parameters);
|
||||
eventEntries.Add(
|
||||
new EventEntry {
|
||||
Category = ev.Category,
|
||||
Type = ev.Type,
|
||||
EventRecordId = eventRecord.Id,
|
||||
DisplayText = ev.Display(new EventContext { Properties = eventParameters })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.Events = eventEntries;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load actions
|
||||
var actionEntries = new List<ActionEntry>();
|
||||
var allActions = _rulesManager.DescribeActions().SelectMany(x => x.Descriptors);
|
||||
|
||||
foreach (var actionRecord in rule.Actions.OrderBy(x => x.Position)) {
|
||||
var category = actionRecord.Category;
|
||||
var type = actionRecord.Type;
|
||||
|
||||
var action = allActions.Where(x => category == x.Category && type == x.Type).FirstOrDefault();
|
||||
if (action != null) {
|
||||
var actionParameters = FormParametersHelper.FromString(actionRecord.Parameters);
|
||||
actionEntries.Add(
|
||||
new ActionEntry {
|
||||
Category = action.Category,
|
||||
Type = action.Type,
|
||||
ActionRecordId = actionRecord.Id,
|
||||
DisplayText = action.Display(new ActionContext { Properties = actionParameters })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.Actions = actionEntries;
|
||||
#endregion
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
[FormValueRequired("submit.Save")]
|
||||
public ActionResult EditPost(EditRuleViewModel viewModel) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
}
|
||||
else {
|
||||
var rule = _rulesServices.GetRule(viewModel.Id);
|
||||
rule.Name = viewModel.Name;
|
||||
rule.Enabled = viewModel.Enabled;
|
||||
|
||||
Services.Notifier.Information(T("Rule Saved"));
|
||||
return RedirectToAction("Edit", new { id = rule.Id });
|
||||
}
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
[FormValueRequired("submit.SaveAndEnable")]
|
||||
public ActionResult EditAndEnablePost(EditRuleViewModel viewModel) {
|
||||
viewModel.Enabled = true;
|
||||
return EditPost(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
|
||||
if (rule != null) {
|
||||
_rulesServices.DeleteRule(id);
|
||||
Services.Notifier.Information(T("Rule {0} deleted", rule.Name));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult Enable(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
|
||||
if (rule != null) {
|
||||
rule.Enabled = true;
|
||||
Services.Notifier.Information(T("Rule enabled"));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public ActionResult Disable(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
|
||||
if (rule != null) {
|
||||
rule.Enabled = false;
|
||||
Services.Notifier.Information(T("Rule disabled"));
|
||||
}
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Rules.ViewModels;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Rules.Controllers {
|
||||
[ValidateInput(false), Admin]
|
||||
public class EventController : Controller {
|
||||
public EventController(
|
||||
IOrchardServices services,
|
||||
IRulesManager rulesManager,
|
||||
IRulesServices rulesServices,
|
||||
IFormManager formManager,
|
||||
IShapeFactory shapeFactory) {
|
||||
Services = services;
|
||||
_rulesManager = rulesManager;
|
||||
_rulesServices = rulesServices;
|
||||
_formManager = formManager;
|
||||
Shape = shapeFactory;
|
||||
}
|
||||
|
||||
public IOrchardServices Services { get; set; }
|
||||
private readonly IRulesManager _rulesManager;
|
||||
private readonly IRulesServices _rulesServices;
|
||||
private readonly IFormManager _formManager;
|
||||
public Localizer T { get; set; }
|
||||
public dynamic Shape { get; set; }
|
||||
|
||||
public ActionResult Add(int id) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var viewModel = new AddEventViewModel { Id = id, Events = _rulesManager.DescribeEvents() };
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
public ActionResult Delete(int id, int eventId) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
_rulesServices.DeleteEvent(eventId);
|
||||
Services.Notifier.Information(T("Event Deleted"));
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
public ActionResult Edit(int id, string category, string type, int eventId = -1) {
|
||||
if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage rules")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var ev = _rulesManager.DescribeEvents().SelectMany(x => x.Descriptors).Where(x => x.Category == category && x.Type == type).FirstOrDefault();
|
||||
|
||||
if (ev == null) {
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
// if there is no form to edit, save the action and go back to the rule
|
||||
if (ev.Form == null) {
|
||||
if (eventId == -1) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
rule.Events.Add(new EventRecord { Category = category, Type = type });
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
// build the form, and let external components alter it
|
||||
var form = _formManager.Build(ev.Form);
|
||||
|
||||
// generate an anti forgery token
|
||||
AddSubmitButton(form);
|
||||
|
||||
// bind form with existing values).
|
||||
if (eventId != -1) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
var eventRecord = rule.Events.Where(a => a.Id == eventId).FirstOrDefault();
|
||||
if (eventRecord != null) {
|
||||
var parameters = FormParametersHelper.FromString(eventRecord.Parameters);
|
||||
_formManager.Bind(form, new DictionaryValueProvider<string>(parameters, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
var viewModel = new EditEventViewModel { Id = id, Event = ev, Form = form };
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("Edit")]
|
||||
public ActionResult EditPost(int id, string category, string type, [DefaultValue(-1)] int eventId, FormCollection formCollection) {
|
||||
var rule = _rulesServices.GetRule(id);
|
||||
|
||||
var eventRecord = rule.Events.Where(a => a.Id == eventId).FirstOrDefault();
|
||||
|
||||
// add new event record if it's a newly created event
|
||||
if (eventRecord == null) {
|
||||
eventRecord = new EventRecord { Category = category, Type = type };
|
||||
rule.Events.Add(eventRecord);
|
||||
}
|
||||
|
||||
var e = _rulesManager.DescribeEvents().SelectMany(x => x.Descriptors).Where(x => x.Category == category && x.Type == type).FirstOrDefault();
|
||||
|
||||
// validating form values
|
||||
_formManager.Validate(new ValidatingContext { FormName = e.Form, ModelState = ModelState, ValueProvider = ValueProvider });
|
||||
|
||||
if (ModelState.IsValid) {
|
||||
var dictionary = formCollection.AllKeys.ToDictionary(key => key, formCollection.Get);
|
||||
|
||||
// save form parameters
|
||||
eventRecord.Parameters = FormParametersHelper.ToString(dictionary);
|
||||
|
||||
return RedirectToAction("Edit", "Admin", new { id });
|
||||
}
|
||||
|
||||
// model is invalid, display it again
|
||||
var form = _formManager.Build(e.Form);
|
||||
AddSubmitButton(form);
|
||||
_formManager.Bind(form, formCollection);
|
||||
var viewModel = new EditEventViewModel { Id = id, Event = e, Form = form };
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
private void AddSubmitButton(dynamic form) {
|
||||
var viewContext = new ViewContext { HttpContext = HttpContext, Controller = this };
|
||||
var token = new HtmlHelper(viewContext, new ViewDataContainer()).AntiForgeryToken();
|
||||
|
||||
// add a submit button to the form
|
||||
form
|
||||
._Actions(Shape.Fieldset(
|
||||
_RequestAntiForgeryToken: Shape.Markup(
|
||||
Value: token.ToString()),
|
||||
_Save: Shape.Submit(
|
||||
Name: "op",
|
||||
Value: T("Save"))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private class ViewDataContainer : IViewDataContainer {
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
|
||||
namespace Orchard.Rules.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class ScheduledActionTaskHandler : IScheduledTaskHandler {
|
||||
private readonly IRulesManager _rulesManager;
|
||||
|
||||
public ScheduledActionTaskHandler(
|
||||
IRulesManager rulesManager) {
|
||||
_rulesManager = rulesManager;
|
||||
}
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public void Process(ScheduledTaskContext context) {
|
||||
if (context.Task.TaskType == "TriggerRule") {
|
||||
Logger.Information("Triggering Rule item #{0} version {1} scheduled at {2} utc",
|
||||
context.Task.ContentItem.Id,
|
||||
context.Task.ContentItem.Version,
|
||||
context.Task.ScheduledUtc);
|
||||
|
||||
var scheduledActionTask = context.Task.ContentItem.As<ScheduledActionTaskPart>();
|
||||
|
||||
_rulesManager.ExecuteActions(scheduledActionTask.ScheduledActions.Select(x => x.ActionRecord), new Dictionary<string, object>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Data;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.Handlers {
|
||||
[UsedImplicitly]
|
||||
public class ScheduledActionTaskPartHandler : ContentHandler {
|
||||
public ScheduledActionTaskPartHandler(IRepository<ScheduledActionTaskRecord> repository)
|
||||
{
|
||||
Filters.Add(StorageFilter.For(repository));
|
||||
Filters.Add(new ActivatingFilter<ScheduledActionTaskPart>("ScheduledActionTask"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.Rules.ImportExport {
|
||||
public interface ICustomExportStep : IEventHandler {
|
||||
void Register(IList<string> steps);
|
||||
}
|
||||
|
||||
public class RulesCustomExportStep : ICustomExportStep {
|
||||
public void Register(IList<string> steps) {
|
||||
steps.Add("Rules");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Orchard.Events;
|
||||
using Orchard.Rules.Services;
|
||||
|
||||
namespace Orchard.Rules.ImportExport {
|
||||
public interface IExportEventHandler : IEventHandler {
|
||||
void Exporting(dynamic context);
|
||||
void Exported(dynamic context);
|
||||
}
|
||||
|
||||
public class RulesExportHandler : IExportEventHandler {
|
||||
private readonly IRulesServices _rulesServices;
|
||||
|
||||
public RulesExportHandler(IRulesServices rulesServices) {
|
||||
_rulesServices = rulesServices;
|
||||
}
|
||||
|
||||
public void Exporting(dynamic context) {
|
||||
}
|
||||
|
||||
public void Exported(dynamic context) {
|
||||
|
||||
if (!((IEnumerable<string>)context.ExportOptions.CustomSteps).Contains("Rules")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var allRules = _rulesServices.GetRules().ToList();
|
||||
|
||||
if(!allRules.Any()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var root = new XElement("Rules");
|
||||
context.Document.Element("Orchard").Add(root);
|
||||
|
||||
foreach(var rule in allRules) {
|
||||
root.Add(new XElement("Rule",
|
||||
new XAttribute("Name", rule.Name),
|
||||
new XAttribute("Enabled", rule.Enabled.ToString(CultureInfo.InvariantCulture)),
|
||||
new XElement("Actions", rule.Actions.Select(action =>
|
||||
new XElement("Action",
|
||||
new XAttribute("Type", action.Type ?? string.Empty),
|
||||
new XAttribute("Category", action.Category ?? string.Empty),
|
||||
new XAttribute("Parameters", action.Parameters ?? string.Empty),
|
||||
new XAttribute("Position", action.Position)
|
||||
)
|
||||
)),
|
||||
new XElement("Events", rule.Events.Select(e =>
|
||||
new XElement("Event",
|
||||
new XAttribute("Type", e.Type ?? string.Empty),
|
||||
new XAttribute("Category", e.Category ?? string.Empty),
|
||||
new XAttribute("Parameters", e.Parameters ?? string.Empty)
|
||||
)
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Recipes.Models;
|
||||
using Orchard.Recipes.Services;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
|
||||
namespace Orchard.Rules.ImportExport {
|
||||
public class RulesRecipeHandler : IRecipeHandler {
|
||||
private readonly IRulesServices _rulesServices;
|
||||
|
||||
public RulesRecipeHandler(IRulesServices rulesServices) {
|
||||
_rulesServices = rulesServices;
|
||||
Logger = NullLogger.Instance;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
// <Data />
|
||||
// Import Data
|
||||
public void ExecuteRecipeStep(RecipeContext recipeContext) {
|
||||
if (!String.Equals(recipeContext.RecipeStep.Name, "Rules", StringComparison.OrdinalIgnoreCase)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var rule in recipeContext.RecipeStep.Step.Elements()) {
|
||||
|
||||
var ruleRecord = _rulesServices.CreateRule(rule.Attribute("Name").Value);
|
||||
ruleRecord.Enabled = bool.Parse(rule.Attribute("Enabled").Value);
|
||||
|
||||
ruleRecord.Actions = rule.Element("Actions").Elements().Select(action =>
|
||||
new ActionRecord {
|
||||
Type = action.Attribute("Type").Value,
|
||||
Category = action.Attribute("Category").Value,
|
||||
Position = int.Parse(action.Attribute("Position").Value),
|
||||
Parameters = action.Attribute("Parameters").Value,
|
||||
RuleRecord = ruleRecord
|
||||
}).ToList();
|
||||
|
||||
ruleRecord.Events = rule.Element("Events").Elements().Select(action =>
|
||||
new EventRecord {
|
||||
Type = action.Attribute("Type").Value,
|
||||
Category = action.Attribute("Category").Value,
|
||||
Parameters = action.Attribute("Parameters").Value,
|
||||
RuleRecord = ruleRecord
|
||||
}).ToList();
|
||||
|
||||
}
|
||||
|
||||
recipeContext.Executed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/Orchard.Web/Modules/Orchard.Rules/Migrations.cs
Normal file
48
src/Orchard.Web/Modules/Orchard.Rules/Migrations.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Orchard.Data.Migration;
|
||||
|
||||
namespace Orchard.Rules {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
|
||||
public int Create() {
|
||||
SchemaBuilder.CreateTable("RuleRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<bool>("Enabled")
|
||||
.Column<string>("Name", c => c.WithLength(1024))
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("EventRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("Category", c => c.WithLength(64))
|
||||
.Column<string>("Type", c => c.WithLength(64))
|
||||
.Column<string>("Parameters", c => c.Unlimited())
|
||||
.Column<int>("RuleRecord_id")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("ActionRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<string>("Category", c => c.WithLength(64))
|
||||
.Column<string>("Type", c => c.WithLength(64))
|
||||
.Column<string>("Parameters", c => c.Unlimited())
|
||||
.Column<int>("Position")
|
||||
.Column<int>("RuleRecord_id")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("ScheduledActionTaskRecord",
|
||||
table => table
|
||||
.ContentPartVersionRecord()
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("ScheduledActionRecord",
|
||||
table => table
|
||||
.Column<int>("Id", c => c.PrimaryKey().Identity())
|
||||
.Column<int>("ActionRecord_id")
|
||||
.Column<int>("ScheduledActionTaskRecord_id")
|
||||
);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ActionContext {
|
||||
public ActionContext() {
|
||||
Tokens = new Dictionary<string, object>();
|
||||
Properties = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Tokens { get; set; }
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ActionDescriptor {
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public LocalizedString Name { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public Func<ActionContext, bool> Action { get; set; }
|
||||
public string Form { get; set; }
|
||||
public Func<ActionContext, LocalizedString> Display { get; set; }
|
||||
}
|
||||
}
|
||||
12
src/Orchard.Web/Modules/Orchard.Rules/Models/ActionRecord.cs
Normal file
12
src/Orchard.Web/Modules/Orchard.Rules/Models/ActionRecord.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ActionRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Category { get; set; }
|
||||
public virtual string Type { get; set; }
|
||||
public virtual string Parameters { get; set; }
|
||||
public virtual int Position { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual RuleRecord RuleRecord { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class DescribeActionContext {
|
||||
private readonly Dictionary<string, DescribeActionFor> _describes = new Dictionary<string, DescribeActionFor>();
|
||||
|
||||
public IEnumerable<TypeDescriptor<ActionDescriptor>> Describe() {
|
||||
return _describes.Select(kp => new TypeDescriptor<ActionDescriptor> {
|
||||
Category = kp.Key,
|
||||
Name = kp.Value.Name,
|
||||
Description = kp.Value.Description,
|
||||
Descriptors = kp.Value.Types
|
||||
});
|
||||
}
|
||||
|
||||
public DescribeActionFor For(string category) {
|
||||
return For(category, null, null);
|
||||
}
|
||||
|
||||
public DescribeActionFor For(string category, LocalizedString name, LocalizedString description) {
|
||||
DescribeActionFor describeFor;
|
||||
if (!_describes.TryGetValue(category, out describeFor)) {
|
||||
describeFor = new DescribeActionFor(category, name, description);
|
||||
_describes[category] = describeFor;
|
||||
}
|
||||
return describeFor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class DescribeActionFor {
|
||||
private readonly string _category;
|
||||
|
||||
public DescribeActionFor(string category, LocalizedString name, LocalizedString description) {
|
||||
Types = new List<ActionDescriptor>();
|
||||
_category = category;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public LocalizedString Name { get; private set; }
|
||||
public LocalizedString Description { get; private set; }
|
||||
public List<ActionDescriptor> Types { get; private set; }
|
||||
|
||||
public DescribeActionFor Element(string type, LocalizedString name, LocalizedString description, Func<ActionContext, bool> action, Func<ActionContext, LocalizedString> display, string form = null) {
|
||||
Types.Add(new ActionDescriptor { Type = type, Name = name, Description = description, Category = _category, Action = action, Display = display, Form = form });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class DescribeEventContext {
|
||||
private readonly Dictionary<string, DescribeEventFor> _describes = new Dictionary<string, DescribeEventFor>();
|
||||
|
||||
public IEnumerable<TypeDescriptor<EventDescriptor>> Describe() {
|
||||
return _describes.Select(kp => new TypeDescriptor<EventDescriptor> {
|
||||
Category = kp.Key,
|
||||
Name = kp.Value.Name,
|
||||
Description = kp.Value.Description,
|
||||
Descriptors = kp.Value.Types
|
||||
});
|
||||
}
|
||||
|
||||
public DescribeEventFor For(string target) {
|
||||
return For(target, null, null);
|
||||
}
|
||||
|
||||
public DescribeEventFor For(string category, LocalizedString name, LocalizedString description) {
|
||||
DescribeEventFor describeFor;
|
||||
if (!_describes.TryGetValue(category, out describeFor)) {
|
||||
describeFor = new DescribeEventFor(category, name, description);
|
||||
_describes[category] = describeFor;
|
||||
}
|
||||
return describeFor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class DescribeEventFor {
|
||||
private readonly string _category;
|
||||
|
||||
public DescribeEventFor(string category, LocalizedString name, LocalizedString description) {
|
||||
Types = new List<EventDescriptor>();
|
||||
_category = category;
|
||||
Name = name;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public LocalizedString Name { get; private set; }
|
||||
public LocalizedString Description { get; private set; }
|
||||
public List<EventDescriptor> Types { get; private set; }
|
||||
|
||||
public DescribeEventFor Element(string type, LocalizedString name, LocalizedString description, Func<EventContext, bool> condition, Func<EventContext, LocalizedString> display, string form = null) {
|
||||
Types.Add(new EventDescriptor { Type = type, Name = name, Description = description, Category = _category, Condition = condition, Display = display, Form = form });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/Orchard.Web/Modules/Orchard.Rules/Models/EventContext.cs
Normal file
13
src/Orchard.Web/Modules/Orchard.Rules/Models/EventContext.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class EventContext {
|
||||
public EventContext() {
|
||||
Tokens = new Dictionary<string, object>();
|
||||
Properties = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public IDictionary<string, object> Tokens { get; set; }
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class EventDescriptor {
|
||||
public string Category { get; set; } // e.g. Content
|
||||
public string Type { get; set; } // e.g. Created
|
||||
public LocalizedString Name { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public Func<EventContext, bool> Condition { get; set; }
|
||||
public string Form { get; set; }
|
||||
public Func<EventContext, LocalizedString> Display { get; set; }
|
||||
}
|
||||
}
|
||||
11
src/Orchard.Web/Modules/Orchard.Rules/Models/EventRecord.cs
Normal file
11
src/Orchard.Web/Modules/Orchard.Rules/Models/EventRecord.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Orchard.Rules.Models {
|
||||
public class EventRecord {
|
||||
public virtual int Id { get; set; }
|
||||
public virtual string Category { get; set; }
|
||||
public virtual string Type { get; set; }
|
||||
public virtual string Parameters { get; set; }
|
||||
|
||||
// Parent property
|
||||
public virtual RuleRecord RuleRecord { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/Orchard.Web/Modules/Orchard.Rules/Models/RuleRecord.cs
Normal file
24
src/Orchard.Web/Modules/Orchard.Rules/Models/RuleRecord.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class RuleRecord {
|
||||
public RuleRecord() {
|
||||
Events = new List<EventRecord>();
|
||||
Actions = new List<ActionRecord>();
|
||||
}
|
||||
|
||||
public virtual int Id { get; set; }
|
||||
public virtual bool Enabled { get; set; }
|
||||
|
||||
[Required, StringLength(1024)]
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<EventRecord> Events { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ActionRecord> Actions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ScheduledActionRecord {
|
||||
public virtual int Id { get; set; }
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual ActionRecord ActionRecord { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ScheduledActionTaskPart : ContentPart<ScheduledActionTaskRecord> {
|
||||
public virtual IList<ScheduledActionRecord> ScheduledActions {
|
||||
get { return Record.ScheduledActions; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.ContentManagement.Records;
|
||||
using Orchard.Data.Conventions;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class ScheduledActionTaskRecord : ContentPartVersionRecord {
|
||||
public ScheduledActionTaskRecord() {
|
||||
ScheduledActions = new List<ScheduledActionRecord>();
|
||||
}
|
||||
|
||||
[CascadeAllDeleteOrphan]
|
||||
public virtual IList<ScheduledActionRecord> ScheduledActions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Models {
|
||||
public class TypeDescriptor<T> {
|
||||
public string Category { get; set; }
|
||||
public LocalizedString Name { get; set; }
|
||||
public LocalizedString Description { get; set; }
|
||||
public IEnumerable<T> Descriptors { get; set; }
|
||||
}
|
||||
}
|
||||
13
src/Orchard.Web/Modules/Orchard.Rules/Module.txt
Normal file
13
src/Orchard.Web/Modules/Orchard.Rules/Module.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
Name: Rules
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardrules.codeplex.com
|
||||
Version: 1.5
|
||||
OrchardVersion: 1.4
|
||||
Description: Provides a system de trigger actions based on events.
|
||||
Features:
|
||||
Orchard.Rules:
|
||||
Name: Rules
|
||||
Description: Provides a system de trigger actions based on events.
|
||||
Dependencies: Orchard.Tokens, Orchard.Scripting, Orchard.Forms
|
||||
Category: Rules
|
||||
205
src/Orchard.Web/Modules/Orchard.Rules/Orchard.Rules.csproj
Normal file
205
src/Orchard.Web/Modules/Orchard.Rules/Orchard.Rules.csproj
Normal file
@@ -0,0 +1,205 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{966EC390-3C7F-4D98-92A6-F0F30D02E9B1}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.Rules</RootNamespace>
|
||||
<AssemblyName>Orchard.Rules</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ClaySharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.Routing" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\Admin\images\offline.gif" />
|
||||
<Content Include="Content\Admin\images\online.gif" />
|
||||
<Content Include="Placement.info">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="Views\Admin\Create.cshtml" />
|
||||
<Content Include="Content\Web.config">
|
||||
<SubType>Designer</SubType>
|
||||
</Content>
|
||||
<Content Include="Views\Admin\Edit.cshtml" />
|
||||
<Content Include="Views\Action\Add.cshtml" />
|
||||
<Content Include="Views\Action\Edit.cshtml" />
|
||||
<Content Include="Views\Admin\Index.cshtml" />
|
||||
<Content Include="Styles\admin-rules.css" />
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Scripts\Web.config" />
|
||||
<Content Include="Styles\Web.config" />
|
||||
<Compile Include="ImportExport\RulesExportEventHandler.cs" />
|
||||
<Compile Include="ImportExport\RulesCustomExportStep.cs" />
|
||||
<Compile Include="ImportExport\RulesRecipeHandler.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Content Include="Module.txt" />
|
||||
<Content Include="Web.config">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</Content>
|
||||
<Content Include="Views\Event\Add.cshtml" />
|
||||
<Content Include="Views\Event\Edit.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<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.Scripting\Orchard.Scripting.csproj">
|
||||
<Project>{99002B65-86F7-415E-BF4A-381AA8AB9CCC}</Project>
|
||||
<Name>Orchard.Scripting</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="AdminMenu.cs" />
|
||||
<Compile Include="Controllers\ActionController.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Controllers\EventController.cs" />
|
||||
<Compile Include="Handlers\ScheduledActionTaskPartHandler.cs" />
|
||||
<Compile Include="Handlers\ScheduledActionTaskHandler.cs" />
|
||||
<Compile Include="Models\ActionContext.cs" />
|
||||
<Compile Include="Models\ActionRecord.cs" />
|
||||
<Compile Include="Models\ScheduledActionRecord.cs" />
|
||||
<Compile Include="Models\ScheduledActionTaskPart.cs" />
|
||||
<Compile Include="Models\ScheduledActionTaskRecord.cs" />
|
||||
<Compile Include="Models\EventContext.cs" />
|
||||
<Compile Include="Models\EventRecord.cs" />
|
||||
<Compile Include="Models\DescribeActionContext.cs" />
|
||||
<Compile Include="Models\DescribeActionFor.cs" />
|
||||
<Compile Include="Models\DescribeEventContext.cs" />
|
||||
<Compile Include="Models\DescribeEventFor.cs" />
|
||||
<Compile Include="Models\ActionDescriptor.cs" />
|
||||
<Compile Include="Models\TypeDescriptor.cs" />
|
||||
<Compile Include="Migrations.cs" />
|
||||
<Compile Include="Models\RuleRecord.cs" />
|
||||
<Compile Include="Providers\ScheduleForms.cs" />
|
||||
<Compile Include="Providers\ScheduleActions.cs" />
|
||||
<Compile Include="Providers\NotificationForms.cs" />
|
||||
<Compile Include="Providers\NotificationActions.cs" />
|
||||
<Compile Include="Services\FormParametersHelper.cs" />
|
||||
<Compile Include="Services\RulesServices.cs" />
|
||||
<Compile Include="Services\IActionProvider.cs" />
|
||||
<Compile Include="Services\IEventProvider.cs" />
|
||||
<Compile Include="Services\IRulesManager.cs" />
|
||||
<Compile Include="Services\IRulesServices.cs" />
|
||||
<Compile Include="Services\RulesManager.cs" />
|
||||
<Compile Include="Models\EventDescriptor.cs" />
|
||||
<Compile Include="ViewModels\AddActionViewModel.cs" />
|
||||
<Compile Include="ViewModels\AddEventViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditEventViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditActionViewModel.cs" />
|
||||
<Compile Include="ViewModels\CreateRuleViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditRuleViewModel.cs" />
|
||||
<Compile Include="ViewModels\RulesIndexViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Drivers\" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target> -->
|
||||
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
|
||||
<PropertyGroup>
|
||||
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
|
||||
</PropertyGroup>
|
||||
<!-- If this is an area child project, uncomment the following line:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
-->
|
||||
<!-- If this is an area parent project, uncomment the following lines:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
|
||||
-->
|
||||
</Target>
|
||||
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
|
||||
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>45979</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
2
src/Orchard.Web/Modules/Orchard.Rules/Placement.info
Normal file
2
src/Orchard.Web/Modules/Orchard.Rules/Placement.info
Normal file
@@ -0,0 +1,2 @@
|
||||
<Placement>
|
||||
</Placement>
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.Rules")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("Copyright <20> Outercurve Foundation 2011")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("e073027d-5f83-4338-97a6-ecdc2fd8d630")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.5")]
|
||||
[assembly: AssemblyFileVersion("1.5")]
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
using Orchard.UI.Notify;
|
||||
|
||||
namespace Orchard.Rules.Providers {
|
||||
public class NotificationActions : IActionProvider {
|
||||
private readonly INotifier _notifier;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public NotificationActions(INotifier notifier, ITokenizer tokenizer) {
|
||||
_notifier = notifier;
|
||||
_tokenizer = tokenizer;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeActionContext describe) {
|
||||
describe.For("Notification", T("Notification"), T("Notifications"))
|
||||
.Element(
|
||||
"Notify",
|
||||
T("Notify"),
|
||||
T("Display a message."),
|
||||
context => {
|
||||
var notification = context.Properties["notification"];
|
||||
var message = context.Properties["message"];
|
||||
|
||||
message = _tokenizer.Replace(message, context.Tokens);
|
||||
|
||||
var notificationType = (NotifyType)Enum.Parse(typeof(NotifyType), notification);
|
||||
_notifier.Add(notificationType, T(message));
|
||||
|
||||
return true;
|
||||
},
|
||||
context => T("Displays \"{1}\" as {0}", T(context.Properties["notification"]).Text, T(context.Properties["message"]).Text),
|
||||
"ActionNotify");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Providers {
|
||||
public class NotificationForms : IFormProvider {
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public NotificationForms(IShapeFactory shapeFactory) {
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
context.Form("ActionNotify",
|
||||
shape => Shape.Form(
|
||||
Id: "ActionNotify",
|
||||
_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,113 @@
|
||||
using System;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Data;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Rules.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Services;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Tasks.Scheduling;
|
||||
using Orchard.Tokens;
|
||||
|
||||
namespace Orchard.Rules.Providers {
|
||||
public class ScheduleActions : IActionProvider {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IScheduledTaskManager _scheduledTaskManager;
|
||||
private readonly IRepository<RuleRecord> _repository;
|
||||
private readonly IRepository<ActionRecord> _actionRecordRepository;
|
||||
private readonly IRepository<ScheduledActionRecord> _scheduledActionRecordRepository;
|
||||
private readonly IClock _clock;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public ScheduleActions(
|
||||
IContentManager contentManager,
|
||||
IScheduledTaskManager scheduledTaskManager,
|
||||
IRepository<RuleRecord> repository,
|
||||
IRepository<ActionRecord> actionRecordRepository,
|
||||
IRepository<ScheduledActionRecord> scheduledActionRecordRepository,
|
||||
IClock clock,
|
||||
ITokenizer tokenizer) {
|
||||
_contentManager = contentManager;
|
||||
_scheduledTaskManager = scheduledTaskManager;
|
||||
_repository = repository;
|
||||
_actionRecordRepository = actionRecordRepository;
|
||||
_scheduledActionRecordRepository = scheduledActionRecordRepository;
|
||||
_clock = clock;
|
||||
_tokenizer = tokenizer;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(DescribeActionContext context) {
|
||||
context.For("System", T("System"), T("System"))
|
||||
.Element("Delayed", T("Delayed Action"), T("Triggers some actions after a specific amount of time."), CreateDelayedAction, DisplayDelayedAction, "ActionDelay");
|
||||
}
|
||||
|
||||
private LocalizedString DisplayDelayedAction(ActionContext context) {
|
||||
var amount = Convert.ToInt32(context.Properties["Amount"]);
|
||||
var type = context.Properties["Unity"];
|
||||
var ruleId = Convert.ToInt32(context.Properties["RuleId"]);
|
||||
|
||||
var rule = _repository.Get(ruleId);
|
||||
|
||||
return T.Plural("Triggers \"{1}\" in {0} {2}", "Triggers \"{1}\" in {0} {2}s", amount, rule.Name, type);
|
||||
}
|
||||
|
||||
private bool CreateDelayedAction(ActionContext context) {
|
||||
var amount = Convert.ToInt32(context.Properties["Amount"]);
|
||||
var type = context.Properties["Unity"];
|
||||
var ruleId = Convert.ToInt32(context.Properties["RuleId"]);
|
||||
|
||||
var scheduledActionTask = _contentManager.New("ScheduledActionTask").As<ScheduledActionTaskPart>();
|
||||
var rule = _repository.Get(ruleId);
|
||||
|
||||
var when = _clock.UtcNow;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
foreach (var action in rule.Actions) {
|
||||
var actionRecord = new ActionRecord {
|
||||
Category = action.Category,
|
||||
Position = action.Position,
|
||||
Type = action.Type,
|
||||
Parameters = _tokenizer.Replace(action.Parameters, context.Tokens)
|
||||
};
|
||||
|
||||
_actionRecordRepository.Create(actionRecord);
|
||||
|
||||
var scheduledAction = new ScheduledActionRecord { ActionRecord = actionRecord };
|
||||
_scheduledActionRecordRepository.Create(scheduledAction);
|
||||
|
||||
scheduledActionTask.ScheduledActions.Add(scheduledAction);
|
||||
}
|
||||
|
||||
|
||||
_contentManager.Create(scheduledActionTask, VersionOptions.Draft);
|
||||
|
||||
_scheduledTaskManager.CreateTask("TriggerRule", when, scheduledActionTask.ContentItem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Data;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Rules.Models;
|
||||
using System.Linq;
|
||||
|
||||
namespace Orchard.Rules.Providers {
|
||||
public class ScheduleForms : IFormProvider {
|
||||
private readonly IRepository<RuleRecord> _repository;
|
||||
protected dynamic Shape { get; set; }
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public ScheduleForms(IShapeFactory shapeFactory, IRepository<RuleRecord> repository) {
|
||||
_repository = repository;
|
||||
Shape = shapeFactory;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
context.Form("ActionDelay",
|
||||
shape => {
|
||||
var rules = _repository.Table.OrderBy(x => x.Name).ToList();
|
||||
|
||||
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 }),
|
||||
_Rule: Shape.SelectList(
|
||||
Id: "RuleId", Name: "RuleId",
|
||||
Title: T("Rule to trigger"))
|
||||
);
|
||||
|
||||
foreach (var rule in rules) {
|
||||
form._Rule.Add(new SelectListItem { Value = rule.Id.ToString(), Text = rule.Name });
|
||||
}
|
||||
|
||||
return form;
|
||||
}
|
||||
);
|
||||
|
||||
context.Form("ActionSchedule",
|
||||
shape => Shape.Form(
|
||||
Id: "ActionSchedule",
|
||||
_Date: Shape.Textbox(
|
||||
Id: "Date", Name: "Date",
|
||||
Title: T("Date")),
|
||||
_Time: Shape.Textbox(
|
||||
Id: "Time", Name: "Time",
|
||||
Title: T("Time"))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public class ScheduleFormsValitator : FormHandler {
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public override void Validating(ValidatingContext context) {
|
||||
if (context.FormName == "ActionDelay") {
|
||||
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);
|
||||
}
|
||||
|
||||
if (context.ValueProvider.GetValue("RuleId").AttemptedValue == String.Empty) {
|
||||
context.ModelState.AddModelError("RuleId", T("You must select at least one Rule").Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Orchard.Web/Modules/Orchard.Rules/Scripts/Web.config
Normal file
25
src/Orchard.Web/Modules/Orchard.Rules/Scripts/Web.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public static class FormParametersHelper {
|
||||
public static string ToString(IDictionary<string, string> parameters) {
|
||||
var doc = new XDocument();
|
||||
doc.Add(new XElement("Form"));
|
||||
var root = doc.Root;
|
||||
|
||||
if (root == null) {
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
foreach (var entry in parameters) {
|
||||
root.Add(new XElement(XmlConvert.EncodeLocalName(entry.Key), entry.Value));
|
||||
}
|
||||
|
||||
return doc.ToString(SaveOptions.DisableFormatting);
|
||||
}
|
||||
|
||||
public static IDictionary<string, string> FromString(string parameters) {
|
||||
var result = new Dictionary<string, string>();
|
||||
|
||||
if (String.IsNullOrEmpty(parameters)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
var doc = XDocument.Parse(parameters);
|
||||
if (doc.Root == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var element in doc.Root.Elements()) {
|
||||
result.Add(element.Name.LocalName, element.Value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.Events;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public interface IActionProvider : IEventHandler {
|
||||
void Describe(DescribeActionContext describe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Orchard.Events;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public interface IEventProvider : IEventHandler {
|
||||
void Describe(DescribeEventContext describe);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Events;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public interface IRulesManager : IEventHandler {
|
||||
IEnumerable<TypeDescriptor<EventDescriptor>> DescribeEvents();
|
||||
IEnumerable<TypeDescriptor<ActionDescriptor>> DescribeActions();
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a specific Event, and provides the tokens context if the event is
|
||||
/// actually executed
|
||||
/// </summary>
|
||||
/// <param name="category">The category of the event to trigger, e.g. Content</param>
|
||||
/// <param name="type">The type of the event to trigger, e.g. Publish</param>
|
||||
/// <param name="tokensContext">An object containing the tokens context</param>
|
||||
void TriggerEvent(string category, string type, Func<Dictionary<string, object>> tokensContext);
|
||||
|
||||
/// <summary>
|
||||
/// Forces the execution of a set of actions
|
||||
/// </summary>
|
||||
/// <param name="actions">The actions to execute</param>
|
||||
/// <param name="tokens">A dictionary containing the tokens </param>
|
||||
void ExecuteActions(IEnumerable<ActionRecord> actions, Dictionary<string, object> tokens);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public interface IRulesServices : IDependency {
|
||||
RuleRecord CreateRule(string name);
|
||||
RuleRecord GetRule(int id);
|
||||
IEnumerable<RuleRecord> GetRules();
|
||||
|
||||
void DeleteRule(int ruleId);
|
||||
void DeleteEvent(int eventId);
|
||||
void DeleteAction(int actionId);
|
||||
|
||||
void MoveUp(int actionId);
|
||||
void MoveDown(int actionId);
|
||||
}
|
||||
}
|
||||
137
src/Orchard.Web/Modules/Orchard.Rules/Services/RulesManager.cs
Normal file
137
src/Orchard.Web/Modules/Orchard.Rules/Services/RulesManager.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Tokens;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public class RulesManager : IRulesManager {
|
||||
private readonly IRepository<EventRecord> _eventRepository;
|
||||
private readonly IRepository<RuleRecord> _ruleRepository;
|
||||
private readonly IEnumerable<IEventProvider> _eventProviders;
|
||||
private readonly IEnumerable<IActionProvider> _actionProviders;
|
||||
private readonly ITokenizer _tokenizer;
|
||||
|
||||
public RulesManager(
|
||||
IRepository<EventRecord> eventRepository,
|
||||
IRepository<RuleRecord> ruleRepository,
|
||||
IEnumerable<IEventProvider> eventProviders,
|
||||
IEnumerable<IActionProvider> actionProviders,
|
||||
ITokenizer tokenizer) {
|
||||
_eventRepository = eventRepository;
|
||||
_ruleRepository = ruleRepository;
|
||||
_eventProviders = eventProviders;
|
||||
_actionProviders = actionProviders;
|
||||
_tokenizer = tokenizer;
|
||||
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public IEnumerable<TypeDescriptor<EventDescriptor>> DescribeEvents() {
|
||||
var context = new DescribeEventContext();
|
||||
foreach (var provider in _eventProviders) {
|
||||
provider.Describe(context);
|
||||
}
|
||||
return context.Describe();
|
||||
}
|
||||
|
||||
public IEnumerable<TypeDescriptor<ActionDescriptor>> DescribeActions() {
|
||||
var context = new DescribeActionContext();
|
||||
foreach (var provider in _actionProviders) {
|
||||
provider.Describe(context);
|
||||
}
|
||||
return context.Describe();
|
||||
}
|
||||
|
||||
public void TriggerEvent(string category, string type, Func<Dictionary<string, object>> tokensContext) {
|
||||
var tokens = tokensContext();
|
||||
var eventDescriptors = DescribeEvents().SelectMany(x => x.Descriptors).ToList();
|
||||
|
||||
// load corresponding events, as on one Rule several events of the same type could be configured
|
||||
// with different parameters
|
||||
var events = _eventRepository.Table
|
||||
.Where(x => x.Category == category && x.Type == type && x.RuleRecord.Enabled)
|
||||
.ToList() // execute the query at this point of time
|
||||
.Where(e => { // take the first event which has a valid condition
|
||||
var eventCategory = e.Category;
|
||||
var eventType = e.Type;
|
||||
|
||||
// look for the specified Event target/type
|
||||
var descriptor = eventDescriptors.FirstOrDefault(x => eventCategory == x.Category && eventType == x.Type);
|
||||
|
||||
if (descriptor == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var properties = FormParametersHelper.FromString(e.Parameters);
|
||||
var context = new EventContext { Tokens = tokens, Properties = properties };
|
||||
|
||||
// check the condition
|
||||
return descriptor.Condition(context);
|
||||
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// if no events are true simply do nothing and return
|
||||
if(!events.Any()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// load rules too for eager loading
|
||||
var rules = _ruleRepository.Table
|
||||
.Where(x => x.Enabled && x.Events.Any(e => e.Category == category && e.Type == type));
|
||||
|
||||
// evaluate their conditions
|
||||
foreach (var e in events) {
|
||||
var rule = e.RuleRecord;
|
||||
|
||||
ExecuteActions(rule.Actions.OrderBy(x => x.Position), tokens);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecuteActions(IEnumerable<ActionRecord> actions, Dictionary<string, object> tokens) {
|
||||
var actionDescriptors = DescribeActions().SelectMany(x => x.Descriptors).ToList();
|
||||
|
||||
// execute each action associated with this rule
|
||||
foreach (var actionRecord in actions) {
|
||||
var actionCategory = actionRecord.Category;
|
||||
var actionType = actionRecord.Type;
|
||||
|
||||
// look for the specified Event target/type
|
||||
var descriptor = actionDescriptors.FirstOrDefault(x => actionCategory == x.Category && actionType == x.Type);
|
||||
|
||||
if (descriptor == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// evaluate the tokens
|
||||
var parameters = _tokenizer.Replace(actionRecord.Parameters, tokens);
|
||||
|
||||
var properties = FormParametersHelper.FromString(parameters);
|
||||
var context = new ActionContext { Properties = properties, Tokens = tokens };
|
||||
|
||||
// execute the action
|
||||
try {
|
||||
var continuation = descriptor.Action(context);
|
||||
|
||||
// early termination of the actions ?
|
||||
if (!continuation) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
Logger.Error(e, "An action could not be executed.");
|
||||
|
||||
// stop executing other actions
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
101
src/Orchard.Web/Modules/Orchard.Rules/Services/RulesServices.cs
Normal file
101
src/Orchard.Web/Modules/Orchard.Rules/Services/RulesServices.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Rules.Models;
|
||||
using Orchard.Data;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.Services {
|
||||
public class RulesServices : IRulesServices {
|
||||
private readonly IRepository<EventRecord> _eventRepository;
|
||||
private readonly IRepository<ActionRecord> _actionRepository;
|
||||
private readonly IRepository<RuleRecord> _ruleRepository;
|
||||
|
||||
public RulesServices(
|
||||
IRepository<EventRecord> eventRepository,
|
||||
IRepository<ActionRecord> actionRepository,
|
||||
IRepository<RuleRecord> ruleRepository) {
|
||||
_eventRepository = eventRepository;
|
||||
_actionRepository = actionRepository;
|
||||
_ruleRepository = ruleRepository;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public RuleRecord CreateRule(string name) {
|
||||
var ruleRecord = new RuleRecord { Name = name };
|
||||
_ruleRepository.Create(ruleRecord);
|
||||
|
||||
return ruleRecord;
|
||||
}
|
||||
|
||||
public RuleRecord GetRule(int id) {
|
||||
return _ruleRepository.Get(id);
|
||||
}
|
||||
|
||||
public IEnumerable<RuleRecord> GetRules() {
|
||||
return _ruleRepository.Table.ToList();
|
||||
}
|
||||
|
||||
public void DeleteRule(int ruleId) {
|
||||
var e = _ruleRepository.Get(ruleId);
|
||||
if (e != null) {
|
||||
_ruleRepository.Delete(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteEvent(int eventId) {
|
||||
var e = _eventRepository.Get(eventId);
|
||||
if (e != null) {
|
||||
_eventRepository.Delete(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteAction(int actionId) {
|
||||
var a = _actionRepository.Get(actionId);
|
||||
if (a != null) {
|
||||
_actionRepository.Delete(a);
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveUp(int actionId) {
|
||||
var action = _actionRepository.Get(actionId);
|
||||
|
||||
// look for the previous action in order in same rule
|
||||
var previous = _actionRepository.Table
|
||||
.Where(x => x.Position < action.Position && x.RuleRecord.Id == action.RuleRecord.Id)
|
||||
.OrderByDescending(x => x.Position)
|
||||
.FirstOrDefault();
|
||||
|
||||
// nothing to do if already at the top
|
||||
if (previous == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// switch positions
|
||||
var temp = previous.Position;
|
||||
previous.Position = action.Position;
|
||||
action.Position = temp;
|
||||
}
|
||||
|
||||
public void MoveDown(int actionId) {
|
||||
var action = _actionRepository.Get(actionId);
|
||||
|
||||
// look for the next action in order in same rule
|
||||
var next = _actionRepository.Table
|
||||
.Where(x => x.Position > action.Position && x.RuleRecord.Id == action.RuleRecord.Id)
|
||||
.OrderBy(x => x.Position)
|
||||
.FirstOrDefault();
|
||||
|
||||
// nothing to do if already at the end
|
||||
if (next == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// switch positions
|
||||
var temp = next.Position;
|
||||
next.Position = action.Position;
|
||||
action.Position = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Orchard.Web/Modules/Orchard.Rules/Styles/Web.config
Normal file
25
src/Orchard.Web/Modules/Orchard.Rules/Styles/Web.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
td.status-disabled {
|
||||
background: transparent url(../Content/Admin/images/offline.gif) no-repeat right;
|
||||
}
|
||||
|
||||
td.status-enabled {
|
||||
background: transparent url(../Content/Admin/images/online.gif) no-repeat right;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
|
||||
public class AddActionViewModel {
|
||||
public int Id { get; set; }
|
||||
public IEnumerable<TypeDescriptor<ActionDescriptor>> Actions { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
|
||||
public class AddEventViewModel {
|
||||
public int Id { get; set; }
|
||||
public IEnumerable<TypeDescriptor<EventDescriptor>> Events { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
public class CreateRuleViewModel {
|
||||
[Required, StringLength(1024)]
|
||||
public string Name { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
|
||||
public class EditActionViewModel {
|
||||
public int Id { get; set; }
|
||||
public int ActionId { get; set; }
|
||||
public ActionDescriptor Action { get; set; }
|
||||
public dynamic Form { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
|
||||
public class EditEventViewModel {
|
||||
public int Id { get; set; }
|
||||
public int EventId { get; set; }
|
||||
public EventDescriptor Event { get; set; }
|
||||
public dynamic Form { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
public class EditRuleViewModel {
|
||||
public int Id { get; set; }
|
||||
[Required, StringLength(1024)]
|
||||
public string Name { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public IEnumerable<EventEntry> Events { get; set; }
|
||||
public IEnumerable<ActionEntry> Actions { get; set; }
|
||||
}
|
||||
|
||||
public class ActionEntry {
|
||||
public int ActionRecordId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public LocalizedString DisplayText { get; set; }
|
||||
}
|
||||
|
||||
public class EventEntry {
|
||||
public int EventRecordId { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Type { get; set; }
|
||||
public LocalizedString DisplayText { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Rules.Models;
|
||||
|
||||
namespace Orchard.Rules.ViewModels {
|
||||
|
||||
public class RulesIndexViewModel {
|
||||
public IList<RulesEntry> Rules { get; set; }
|
||||
public RulesIndexOptions Options { get; set; }
|
||||
public dynamic Pager { get; set; }
|
||||
}
|
||||
|
||||
public class RulesEntry {
|
||||
public RuleRecord Rule { get; set; }
|
||||
|
||||
public bool IsChecked { get; set; }
|
||||
public int RuleId { get; set; }
|
||||
}
|
||||
|
||||
public class RulesIndexOptions {
|
||||
public string Search { get; set; }
|
||||
public RulesOrder Order { get; set; }
|
||||
public RulesFilter Filter { get; set; }
|
||||
public RulesBulkAction BulkAction { get; set; }
|
||||
}
|
||||
|
||||
public enum RulesOrder {
|
||||
Name,
|
||||
Creation
|
||||
}
|
||||
|
||||
public enum RulesFilter {
|
||||
All,
|
||||
Enabled,
|
||||
Disabled
|
||||
}
|
||||
|
||||
public enum RulesBulkAction {
|
||||
None,
|
||||
Enable,
|
||||
Disable,
|
||||
Delete
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
@model Orchard.Rules.ViewModels.AddActionViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Add an Action");
|
||||
}
|
||||
|
||||
@if (!Model.Actions.Any())
|
||||
{
|
||||
<h3>@T("There are no currently available actions")</h3>
|
||||
}
|
||||
|
||||
@foreach (var action in Model.Actions.OrderBy(x => x.Name.Text))
|
||||
{
|
||||
<h2>@action.Name</h2>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:300px">@T("Name")</th>
|
||||
<th scope="col">@T("Description")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var descriptor in action.Descriptors)
|
||||
{
|
||||
<tr>
|
||||
<td>@Html.ActionLink(descriptor.Name.Text, "Edit", new { id = Model.Id, category = descriptor.Category, type = descriptor.Type })</td>
|
||||
<td>@descriptor.Description</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@model Orchard.Rules.ViewModels.EditActionViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Edit Action - {0}", Model.Action.Name);
|
||||
}
|
||||
|
||||
@Html.ValidationSummary()
|
||||
<h2>@Model.Action.Description</h2>
|
||||
@Display(Model.Form)
|
||||
|
||||
@Display.TokenHint()
|
||||
@@ -0,0 +1,18 @@
|
||||
@model Orchard.Rules.ViewModels.CreateRuleViewModel
|
||||
@{
|
||||
Layout.Title = T("Create New Rule").Text;
|
||||
}
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.Name)
|
||||
@Html.TextBoxFor(m => m.Name, new { @class = "textMedium text" })
|
||||
<span class="hint">@T("The name of the Rule")</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit">@T("Save")</button>
|
||||
</fieldset>
|
||||
}
|
||||
105
src/Orchard.Web/Modules/Orchard.Rules/Views/Admin/Edit.cshtml
Normal file
105
src/Orchard.Web/Modules/Orchard.Rules/Views/Admin/Edit.cshtml
Normal file
@@ -0,0 +1,105 @@
|
||||
@model Orchard.Rules.ViewModels.EditRuleViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Edit Rule").Text;
|
||||
Style.Include("admin-rules.css");
|
||||
}
|
||||
|
||||
@using (Html.BeginFormAntiForgeryPost())
|
||||
{
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<fieldset>
|
||||
@Html.LabelFor(m => m.Name)
|
||||
@Html.TextBoxFor(m => m.Name, new { @class = "textMedium text" })
|
||||
<span class="hint">@T("The name of the Rule")</span>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<h2>@T("Events")</h2>
|
||||
</fieldset>
|
||||
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Add a new Event").Text, "Add", new { controller = "Event", id = Model.Id }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" >@T("Description")</th>
|
||||
<th scope="col" class="actions"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var ev in Model.Events)
|
||||
{
|
||||
<tr>
|
||||
<td>@ev.DisplayText</td>
|
||||
<td>
|
||||
@Html.ActionLink(T("Edit").Text, "Edit", new { controller = "Event", id = Model.Id, category = ev.Category, type = ev.Type, eventId = ev.EventRecordId }) |
|
||||
@Html.ActionLink(T("Delete").Text, "Delete", new { controller = "Event", id = Model.Id, eventId = ev.EventRecordId }, new { itemprop = "RemoveUrl UnsafeUrl" })
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
|
||||
@* Render a button at the bottom only if there are several actions in the table *@
|
||||
if (Model.Events.Count() > 5)
|
||||
{
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Add a new Event").Text, "Add", new { controller = "Event", id = Model.Id }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
}
|
||||
|
||||
<fieldset>
|
||||
|
||||
<fieldset class="bulk-actions">
|
||||
<h2>@T("Actions")</h2>
|
||||
</fieldset>
|
||||
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Add a new Action").Text, "Add", new { controller = "Action", id = Model.Id }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" >@T("Description")</th>
|
||||
<th scope="col" class="actions"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var action in Model.Actions)
|
||||
{
|
||||
<tr>
|
||||
<td>@action.DisplayText</td>
|
||||
<td>
|
||||
@Html.ActionLink(T("Edit").Text, "Edit", new { controller = "Action", id = Model.Id, category = action.Category, type = action.Type, actionId = action.ActionRecordId }) |
|
||||
@Html.ActionLink(T("Delete").Text, "Delete", new { controller = "Action", id = Model.Id, actionId = action.ActionRecordId }, new { itemprop = "RemoveUrl UnsafeUrl" }) |
|
||||
@if(action != Model.Actions.First()) {
|
||||
@Html.ActionLink(T("Up").Text, "Move", new { controller = "Admin", id = Model.Id, direction = "up", actionId = action.ActionRecordId }) @:|
|
||||
}
|
||||
@if(action != Model.Actions.Last()) {
|
||||
@Html.ActionLink(T("Down").Text, "Move", new { controller = "Admin", id = Model.Id, direction = "down", actionId = action.ActionRecordId })
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
|
||||
@* Render a button at the bottom only if there are several actions in the table *@
|
||||
@if (Model.Actions.Count() > 5)
|
||||
{
|
||||
<div class="manage">
|
||||
@Html.ActionLink(T("Add a new Action").Text, "Add", new { controller = "Action", id = Model.Id }, new { @class = "button primaryAction" })
|
||||
</div>
|
||||
}
|
||||
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<button class="primaryAction" type="submit" name="submit.Save" value="submit.Save" >@T("Save")</button>
|
||||
@if (!Model.Enabled) {
|
||||
<button class="primaryAction" type="submit" name="submit.SaveAndEnable" value="submit.SaveAndEnable" >@T("Save and Enable")</button>
|
||||
}
|
||||
</fieldset>
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
@model Orchard.Rules.ViewModels.RulesIndexViewModel
|
||||
@using Orchard.Rules.Models;
|
||||
@using Orchard.Rules.ViewModels;
|
||||
|
||||
@{
|
||||
Style.Include("admin-rules.css");
|
||||
|
||||
var ruleIndex = 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 Rules").ToString()) </h1>
|
||||
@using (Html.BeginFormAntiForgeryPost()) {
|
||||
@Html.ValidationSummary()
|
||||
<div class="manage">@Html.ActionLink(T("Add a new Rule").ToString(), "Create", new { }, 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, RulesBulkAction.None, T("Choose action...").ToString())
|
||||
@Html.SelectOption(Model.Options.BulkAction, RulesBulkAction.Enable, T("Enable").ToString())
|
||||
@Html.SelectOption(Model.Options.BulkAction, RulesBulkAction.Disable, T("Disable").ToString())
|
||||
@Html.SelectOption(Model.Options.BulkAction, RulesBulkAction.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, RulesOrder.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"> ↓</th>
|
||||
<th scope="col">@T("Name")</th>
|
||||
<th scope="col">@T("Creation")</th>
|
||||
<th scope="col"> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var entry in Model.Rules) {
|
||||
<tr>
|
||||
<td class="status-@(entry.Rule.Enabled ? "enabled" : "disabled")">
|
||||
<input type="hidden" value="@Model.Rules[ruleIndex].Rule.Id" name="@Html.NameOf(m => m.Rules[ruleIndex].RuleId)"/>
|
||||
<input type="checkbox" value="true" name="@Html.NameOf(m => m.Rules[ruleIndex].IsChecked)"/>
|
||||
</td>
|
||||
<td>
|
||||
@Html.ActionLink(entry.Rule.Name, "Edit", new { entry.Rule.Id })
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td>
|
||||
@Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.Rule.Id }) |
|
||||
@Html.ActionLink(T("Delete").ToString(), "Delete", new { entry.Rule.Id }, new { itemprop = "RemoveUrl UnsafeUrl" }) |
|
||||
@if(entry.Rule.Enabled == false) {
|
||||
@Html.ActionLink(T("Enable").ToString(), "Enable", new { entry.Rule.Id })
|
||||
} else {
|
||||
@Html.ActionLink(T("Disable").ToString(), "Disable", new { entry.Rule.Id })
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
ruleIndex++;
|
||||
}
|
||||
</table>
|
||||
@Display(Model.Pager)
|
||||
</fieldset>
|
||||
}
|
||||
31
src/Orchard.Web/Modules/Orchard.Rules/Views/Event/Add.cshtml
Normal file
31
src/Orchard.Web/Modules/Orchard.Rules/Views/Event/Add.cshtml
Normal file
@@ -0,0 +1,31 @@
|
||||
@model Orchard.Rules.ViewModels.AddEventViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Add an Event");
|
||||
}
|
||||
|
||||
@if (!Model.Events.Any())
|
||||
{
|
||||
<h3>@T("There are no currently available events")</h3>
|
||||
}
|
||||
|
||||
@foreach (var ev in Model.Events.OrderBy(x => x.Name.Text))
|
||||
{
|
||||
<h2>@ev.Name</h2>
|
||||
|
||||
<table class="items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="width:300px">@T("Name")</th>
|
||||
<th scope="col">@T("Description")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@foreach (var descriptor in ev.Descriptors)
|
||||
{
|
||||
<tr>
|
||||
<td>@Html.ActionLink(descriptor.Name.Text, "Edit", new { id = Model.Id, category = descriptor.Category, type = descriptor.Type })</td>
|
||||
<td>@descriptor.Description</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
@model Orchard.Rules.ViewModels.EditEventViewModel
|
||||
|
||||
@{
|
||||
Layout.Title = T("Edit Event - {0}", Model.Event.Name);
|
||||
}
|
||||
|
||||
@Html.ValidationSummary()
|
||||
<h2>@Model.Event.Description</h2>
|
||||
@Display(Model.Form)
|
||||
|
||||
41
src/Orchard.Web/Modules/Orchard.Rules/Views/Web.config
Normal file
41
src/Orchard.Web/Modules/Orchard.Rules/Views/Web.config
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
Enabling request validation in view pages would cause validation to occur
|
||||
after the input has already been processed by the controller. By default
|
||||
MVC performs request validation before a controller processes the input.
|
||||
To change this behavior apply the ValidateInputAttribute to a
|
||||
controller or action.
|
||||
-->
|
||||
<pages
|
||||
validateRequest="false"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
44
src/Orchard.Web/Modules/Orchard.Rules/Web.config
Normal file
44
src/Orchard.Web/Modules/Orchard.Rules/Web.config
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host" />
|
||||
<remove name="pages" />
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
<add namespace="System.Web.Mvc.Ajax" />
|
||||
<add namespace="System.Web.Mvc.Html" />
|
||||
<add namespace="System.Web.Routing" />
|
||||
<add namespace="System.Web.WebPages" />
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
<add namespace="Orchard.Rules"/>
|
||||
<add namespace="Orchard.Rules.Models"/>
|
||||
<add namespace="Orchard.Rules.ViewModels"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<system.web>
|
||||
<compilation targetFramework="4.0">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user