Including Orchard.Rule

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2012-08-22 15:37:35 -07:00
parent 91a0ed9d7f
commit 407e98dfa6
62 changed files with 2608 additions and 0 deletions

View 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

View 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>

View File

@@ -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"))
)
);
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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; }
}
}
}

View File

@@ -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>());
}
}
}
}

View File

@@ -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"));
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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)
)
))
));
}
}
}
}

View File

@@ -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;
}
}
}

View 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;
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View 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; }
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View 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; }
}
}

View File

@@ -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; }
}
}

View 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; }
}
}

View 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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View 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

View 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>

View File

@@ -0,0 +1,2 @@
<Placement>
</Placement>

View File

@@ -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")]

View File

@@ -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");
}
}
}

View File

@@ -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" })
)
);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}
}

View 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>

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,8 @@
using Orchard.Events;
using Orchard.Rules.Models;
namespace Orchard.Rules.Services {
public interface IActionProvider : IEventHandler {
void Describe(DescribeActionContext describe);
}
}

View File

@@ -0,0 +1,8 @@
using Orchard.Events;
using Orchard.Rules.Models;
namespace Orchard.Rules.Services {
public interface IEventProvider : IEventHandler {
void Describe(DescribeEventContext describe);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View 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;
}
}
}
}
}

View 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;
}
}
}

View 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>

View File

@@ -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;
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Orchard.Rules.ViewModels {
public class CreateRuleViewModel {
[Required, StringLength(1024)]
public string Name { get; set; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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
}
}

View File

@@ -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>
}

View File

@@ -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()

View File

@@ -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>
}

View 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">&nbsp;</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">&nbsp;</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>
}

View File

@@ -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">&nbsp;&darr;</th>
<th scope="col">@T("Name")</th>
<th scope="col">@T("Creation")</th>
<th scope="col">&nbsp;</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>
&nbsp;
</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>
}

View 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>
}

View File

@@ -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)

View 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>

View 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>