mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
- RuleEngine implementation for widget layer rules.
- The ResultFilter uses the rule manager to filter out layers and widgets. - Added rules to default layers. - RuleManager with callback injection into the dlr scripting sandbox. - RuleProvider implementations for Url and Authenticated. Anonymous is just "not Authenticated". - RuleContext. - Renaming Layer.Rule to Layer.LayerRule. NHibernate bug for SqlCe most likely, couldn't escape "Rule" as expected. - Unit tests. --HG-- branch : dev
This commit is contained in:
@@ -63,6 +63,21 @@
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\fluentnhibernate\FluentNHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronRuby, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\dlr\IronRuby.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="IronRuby.Libraries">
|
||||
<HintPath>..\..\lib\dlr\IronRuby.Libraries.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Dynamic, Version=1.1.0.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\dlr\Microsoft.Dynamic.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Scripting, Version=1.1.0.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\dlr\Microsoft.Scripting.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.0.812.4, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\lib\moq\Moq.dll</HintPath>
|
||||
@@ -115,6 +130,7 @@
|
||||
<Compile Include="Values.cs" />
|
||||
<Compile Include="Users\Controllers\AdminControllerTests.cs" />
|
||||
<Compile Include="Users\Services\MembershipServiceTests.cs" />
|
||||
<Compile Include="Widgets\WidgetsTests.cs" />
|
||||
<Compile Include="XmlRpc\Controllers\HomeControllerTests.cs" />
|
||||
<Compile Include="XmlRpc\Services\XmlRpcReaderTests.cs" />
|
||||
<Compile Include="XmlRpc\Services\XmlRpcWriterTests.cs" />
|
||||
@@ -144,6 +160,10 @@
|
||||
<Project>{79AED36E-ABD0-4747-93D3-8722B042454B}</Project>
|
||||
<Name>Orchard.Users</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Widgets\Orchard.Widgets.csproj">
|
||||
<Project>{194D3CCC-1153-474D-8176-FDE8D7D0D0BD}</Project>
|
||||
<Name>Orchard.Widgets</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
|
56
src/Orchard.Tests.Modules/Widgets/WidgetsTests.cs
Normal file
56
src/Orchard.Tests.Modules/Widgets/WidgetsTests.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Scripting;
|
||||
using Orchard.UI.Widgets;
|
||||
using Orchard.Widgets.RuleEngine;
|
||||
|
||||
namespace Orchard.Tests.Modules.Widgets {
|
||||
[TestFixture]
|
||||
public class WidgetsTests {
|
||||
private IContainer _container;
|
||||
private IRuleManager _ruleManager;
|
||||
|
||||
[SetUp]
|
||||
public void Init() {
|
||||
var builder = new ContainerBuilder();
|
||||
builder.RegisterType<ScriptingRuntime>().As<IScriptingRuntime>();
|
||||
builder.RegisterType<ScriptingManager>().As<IScriptingManager>();
|
||||
builder.RegisterType<AlwaysTrueRuleProvider>().As<IRuleProvider>();
|
||||
builder.RegisterType<RuleManager>().As<IRuleManager>();
|
||||
_container = builder.Build();
|
||||
_ruleManager = _container.Resolve<IRuleManager>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProviderGetsCalledForExpression() {
|
||||
bool result = _ruleManager.Matches("hello");
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RubyExpressionIsEvaluated() {
|
||||
bool result = _ruleManager.Matches("not hello");
|
||||
Assert.IsFalse(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ArgumentsArePassedCorrectly() {
|
||||
bool result = _ruleManager.Matches("add(2, 3) == 5");
|
||||
Assert.IsTrue(result);
|
||||
}
|
||||
}
|
||||
|
||||
public class AlwaysTrueRuleProvider : IRuleProvider {
|
||||
public void Process(RuleContext ruleContext) {
|
||||
if (ruleContext.FunctionName == "add") {
|
||||
ruleContext.Result = Convert.ToInt32(ruleContext.Arguments[0]) + Convert.ToInt32(ruleContext.Arguments[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
ruleContext.Result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,23 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Mvc.Filters;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.UI.Widgets;
|
||||
using Orchard.Widgets.Models;
|
||||
|
||||
namespace Orchard.Widgets.Filters {
|
||||
public class WidgetFilter : FilterProvider, IResultFilter {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly IRuleManager _ruleManager;
|
||||
|
||||
public WidgetFilter(IContentManager contentManager, IWorkContextAccessor workContextAccessor) {
|
||||
public WidgetFilter(IContentManager contentManager, IWorkContextAccessor workContextAccessor, IRuleManager ruleManager) {
|
||||
_contentManager = contentManager;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_ruleManager = ruleManager;
|
||||
}
|
||||
|
||||
public void OnResultExecuting(ResultExecutingContext filterContext) {
|
||||
@@ -30,13 +33,22 @@ namespace Orchard.Widgets.Filters {
|
||||
// Once the Rule Engine is done:
|
||||
// Get Layers and filter by zone and rule
|
||||
IEnumerable<WidgetPart> widgetParts = _contentManager.Query<WidgetPart, WidgetPartRecord>().List();
|
||||
IEnumerable<LayerPart> activeLayers = _contentManager.Query<LayerPart, LayerPartRecord>().List();
|
||||
|
||||
List<int> activeLayerIds = new List<int>();
|
||||
foreach (var activeLayer in activeLayers) {
|
||||
if (_ruleManager.Matches(activeLayer.Record.LayerRule)) {
|
||||
activeLayerIds.Add(activeLayer.ContentItem.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// Build and add shape to zone.
|
||||
var zones = workContext.Page.Zones;
|
||||
foreach (var widgetPart in widgetParts) {
|
||||
var widgetShape = _contentManager.BuildDisplayModel(widgetPart);
|
||||
|
||||
zones[widgetPart.Record.Zone].Add(widgetShape, widgetPart.Record.Position);
|
||||
if (activeLayerIds.Contains(widgetPart.As<ICommonPart>().Container.ContentItem.Id)) {
|
||||
var widgetShape = _contentManager.BuildDisplayModel(widgetPart);
|
||||
zones[widgetPart.Record.Zone].Add(widgetShape, widgetPart.Record.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -21,9 +21,12 @@ namespace Orchard.Widgets {
|
||||
}
|
||||
|
||||
public void CreateDefaultLayers() {
|
||||
_contentManager.Create<LayerPart>("Layer", t => t.Record.Name = "Default");
|
||||
_contentManager.Create<LayerPart>("Layer", t => t.Record.Name = "Authenticated");
|
||||
_contentManager.Create<LayerPart>("Layer", t => t.Record.Name = "Anonymous");
|
||||
IContent defaultLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Default"; t.Record.LayerRule = "true"; });
|
||||
_contentManager.Publish(defaultLayer.ContentItem);
|
||||
IContent authenticatedLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Authenticated"; t.Record.LayerRule = "Authenticated"; });
|
||||
_contentManager.Publish(authenticatedLayer.ContentItem);
|
||||
IContent anonymousLayer = _contentManager.Create<LayerPart>("Layer", t => { t.Record.Name = "Anonymous"; t.Record.LayerRule = "not Authenticated"; });
|
||||
_contentManager.Publish(anonymousLayer.ContentItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +46,7 @@ namespace Orchard.Widgets {
|
||||
.ContentPartRecord()
|
||||
.Column<string>("Name")
|
||||
.Column<string>("Description")
|
||||
.Column<string>("Rule")
|
||||
.Column<string>("LayerRule")
|
||||
);
|
||||
|
||||
SchemaBuilder.CreateTable("WidgetPartRecord", table => table
|
||||
|
@@ -4,6 +4,6 @@ namespace Orchard.Widgets.Models {
|
||||
public class LayerPartRecord : ContentPartRecord {
|
||||
public virtual string Name { get; set; }
|
||||
public virtual string Description { get; set; }
|
||||
public virtual string Rule { get; set; }
|
||||
public virtual string LayerRule { get; set; }
|
||||
}
|
||||
}
|
@@ -69,6 +69,9 @@
|
||||
<Compile Include="Permissions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Filters\WidgetFilter.cs" />
|
||||
<Compile Include="RuleEngine\AuthenticatedRuleProvider.cs" />
|
||||
<Compile Include="RuleEngine\RuleManager.cs" />
|
||||
<Compile Include="RuleEngine\UrlRuleProvider.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Module.txt" />
|
||||
|
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI.Widgets;
|
||||
|
||||
namespace Orchard.Widgets.RuleEngine {
|
||||
public class AuthenticatedRuleProvider : IRuleProvider {
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
|
||||
public AuthenticatedRuleProvider(IAuthenticationService authenticationService) {
|
||||
_authenticationService = authenticationService;
|
||||
}
|
||||
|
||||
public void Process(RuleContext ruleContext) {
|
||||
if (!String.Equals(ruleContext.FunctionName, "Authenticated", StringComparison.OrdinalIgnoreCase)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_authenticationService.GetAuthenticatedUser() != null) {
|
||||
ruleContext.Result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ruleContext.Result = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.Scripting;
|
||||
using Orchard.UI.Widgets;
|
||||
|
||||
namespace Orchard.Widgets.RuleEngine {
|
||||
public class RuleManager : IRuleManager {
|
||||
private readonly IEnumerable<IRuleProvider> _ruleProviders;
|
||||
private readonly IScriptingManager _scriptingManager;
|
||||
|
||||
public RuleManager(IEnumerable<IRuleProvider> ruleProviders, IScriptingManager scriptingManager) {
|
||||
_ruleProviders = ruleProviders;
|
||||
_scriptingManager = scriptingManager;
|
||||
}
|
||||
|
||||
public bool Matches(string expression) {
|
||||
_scriptingManager.SetVariable("callbacks", new CallbackApi(this));
|
||||
dynamic execContext = _scriptingManager.Eval(@"
|
||||
class ExecContext
|
||||
def initialize(callbacks)
|
||||
@callbacks = callbacks;
|
||||
end
|
||||
|
||||
def execute(text)
|
||||
instance_eval(text.to_s);
|
||||
end
|
||||
|
||||
def method_missing(name, *args, &block)
|
||||
@callbacks.send(name, args, &block);
|
||||
end
|
||||
end
|
||||
ExecContext.new(callbacks)");
|
||||
return execContext.execute(expression);
|
||||
}
|
||||
|
||||
public class CallbackApi {
|
||||
private readonly RuleManager _ruleManager;
|
||||
|
||||
public CallbackApi(RuleManager ruleManager) {
|
||||
_ruleManager = ruleManager;
|
||||
}
|
||||
|
||||
public object send(string name, IList<object> args) {
|
||||
return _ruleManager.Evaluate(name, args);
|
||||
}
|
||||
}
|
||||
|
||||
private object Evaluate(string name, IList<object> args) {
|
||||
RuleContext ruleContext = new RuleContext {FunctionName = name, Arguments = args.ToArray()};
|
||||
|
||||
foreach (var ruleProvider in _ruleProviders) {
|
||||
ruleProvider.Process(ruleContext);
|
||||
}
|
||||
|
||||
return ruleContext.Result;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Orchard.UI.Widgets;
|
||||
|
||||
namespace Orchard.Widgets.RuleEngine {
|
||||
public class UrlRuleProvider : IRuleProvider {
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
|
||||
public UrlRuleProvider(IWorkContextAccessor workContextAccessor) {
|
||||
_workContextAccessor = workContextAccessor;
|
||||
}
|
||||
|
||||
public void Process(RuleContext ruleContext) {
|
||||
if (!String.Equals(ruleContext.FunctionName, "Url", StringComparison.OrdinalIgnoreCase)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = _workContextAccessor.GetContext();
|
||||
ruleContext.Result = context.HttpContext.Request.RawUrl.StartsWith(Convert.ToString(ruleContext.Arguments[0]));
|
||||
}
|
||||
}
|
||||
}
|
@@ -179,6 +179,9 @@
|
||||
<Compile Include="Settings\ResourceDebugMode.cs" />
|
||||
<Compile Include="UI\Resources\IResourceManifestProvider.cs" />
|
||||
<Compile Include="UI\Resources\ResourceManifestBuilder.cs" />
|
||||
<Compile Include="UI\Widgets\IRuleManager.cs" />
|
||||
<Compile Include="UI\Widgets\IRuleProvider.cs" />
|
||||
<Compile Include="UI\Widgets\RuleContext.cs" />
|
||||
<Compile Include="UI\Zones\PageWorkContext.cs" />
|
||||
<Compile Include="UI\Zones\ZoneHoldingBehavior.cs" />
|
||||
<Compile Include="Mvc\ViewEngines\ThemeAwareness\ConfiguredEnginesCache.cs" />
|
||||
|
5
src/Orchard/UI/Widgets/IRuleManager.cs
Normal file
5
src/Orchard/UI/Widgets/IRuleManager.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Orchard.UI.Widgets {
|
||||
public interface IRuleManager : IDependency {
|
||||
bool Matches(string expression);
|
||||
}
|
||||
}
|
6
src/Orchard/UI/Widgets/IRuleProvider.cs
Normal file
6
src/Orchard/UI/Widgets/IRuleProvider.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Orchard.UI.Widgets {
|
||||
public interface IRuleProvider : IDependency {
|
||||
void Process(RuleContext ruleContext);
|
||||
}
|
||||
}
|
||||
|
7
src/Orchard/UI/Widgets/RuleContext.cs
Normal file
7
src/Orchard/UI/Widgets/RuleContext.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Orchard.UI.Widgets {
|
||||
public class RuleContext {
|
||||
public string FunctionName { get; set; }
|
||||
public object[] Arguments { get; set; }
|
||||
public object Result { get; set; }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user