diff --git a/src/Orchard.Tests.Modules/Conditions/ConditionEvaluationTests.cs b/src/Orchard.Tests.Modules/Conditions/ConditionEvaluationTests.cs new file mode 100644 index 000000000..76ececaea --- /dev/null +++ b/src/Orchard.Tests.Modules/Conditions/ConditionEvaluationTests.cs @@ -0,0 +1,57 @@ +using System; +using Autofac; +using NUnit.Framework; +using Orchard.Caching; +using Orchard.Conditions.Services; +using Orchard.Scripting; +using Orchard.Tests.Stubs; + +namespace Orchard.Tests.Modules.Conditions { + [TestFixture] + public class ConditionEvaluationTests { + private IContainer _container; + private IConditionManager _conditionManager; + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + + _container = builder.Build(); + _conditionManager = _container.Resolve(); + } + + [Test] + public void ProviderGetsCalledForExpression() { + var result = _conditionManager.Matches("hello"); + Assert.IsTrue(result); + } + + [Test] + public void RubyExpressionIsEvaluated() { + var result = _conditionManager.Matches("not hello"); + Assert.IsFalse(result); + } + + [Test] + public void ArgumentsArePassedCorrectly() { + var result = _conditionManager.Matches("add(2, 3) == 5"); + Assert.IsTrue(result); + } + } + + public class AlwaysTrueCondition : IConditionProvider { + public void Evaluate(ConditionEvaluationContext evaluationContext) { + if (evaluationContext.FunctionName == "add") { + evaluationContext.Result = Convert.ToInt32(evaluationContext.Arguments[0]) + Convert.ToInt32(evaluationContext.Arguments[1]); + return; + } + + evaluationContext.Result = true; + } + } +} + diff --git a/src/Orchard.Tests.Modules/Widgets/RuleEngine/UrlRuleProviderTest.cs b/src/Orchard.Tests.Modules/Conditions/Providers/UrlRuleConditionTest.cs similarity index 62% rename from src/Orchard.Tests.Modules/Widgets/RuleEngine/UrlRuleProviderTest.cs rename to src/Orchard.Tests.Modules/Conditions/Providers/UrlRuleConditionTest.cs index f70dd00b6..f77b7e790 100644 --- a/src/Orchard.Tests.Modules/Widgets/RuleEngine/UrlRuleProviderTest.cs +++ b/src/Orchard.Tests.Modules/Conditions/Providers/UrlRuleConditionTest.cs @@ -1,87 +1,87 @@ -using System; -using Autofac; -using NUnit.Framework; -using Orchard.Environment.Configuration; -using Orchard.Mvc; -using Orchard.Tests.Stubs; -using Orchard.Widgets.RuleEngine; -using Orchard.Widgets.Services; - -namespace Orchard.Tests.Modules.Widgets.RuleEngine { - [TestFixture] - public class UrlRuleProviderTest { - private IContainer _container; - private IRuleProvider _urlRuleProvider; - private StubHttpContextAccessor _stubContextAccessor; - private ShellSettings _shellSettings; - - [SetUp] - public void Init() { - var builder = new ContainerBuilder(); - _shellSettings = new ShellSettings { RequestUrlPrefix = String.Empty }; - builder.RegisterType().As(); - builder.RegisterInstance(_shellSettings); - _stubContextAccessor = new StubHttpContextAccessor(); - builder.RegisterInstance(_stubContextAccessor).As(); - _container = builder.Build(); - _urlRuleProvider = _container.Resolve(); - } - - [Test] - public void UrlForHomePageMatchesHomePagePath() { - _stubContextAccessor.Set(new StubHttpContext("~/")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - - [Test] - public void UrlForAboutPageMatchesAboutPagePath() { - _stubContextAccessor.Set(new StubHttpContext("~/about")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/about" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - - [Test] - public void UrlForBlogWithEndingWildcardMatchesBlogPostPageInSaidBlog() { - _stubContextAccessor.Set(new StubHttpContext("~/my-blog/my-blog-post")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/my-blog/*" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - - [Test] - public void UrlForHomePageDoesNotMatchAboutPagePath() { - _stubContextAccessor.Set(new StubHttpContext("~/about")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.False); - } - - [Test] - public void UrlForAboutPageMatchesDifferentCasedAboutPagePath() { - _stubContextAccessor.Set(new StubHttpContext("~/About")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/about" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - - [Test] - public void UrlForAboutPageWithEndingSlashMatchesAboutPagePath() { - _stubContextAccessor.Set(new StubHttpContext("~/About/")); - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/about" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - - [Test] - public void UrlForHomePageMatchesHomePagePathWithUrlPrefix() { - _stubContextAccessor.Set(new StubHttpContext("~/site1")); - _shellSettings.RequestUrlPrefix = "site1"; - var context = new RuleContext { FunctionName = "url", Arguments = new[] { "~/" } }; - _urlRuleProvider.Process(context); - Assert.That(context.Result, Is.True); - } - } +using System; +using Autofac; +using NUnit.Framework; +using Orchard.Conditions.Services; +using Orchard.Environment.Configuration; +using Orchard.Mvc; +using Orchard.Tests.Stubs; +using Orchard.Widgets.RuleEngine; + +namespace Orchard.Tests.Modules.Conditions.Providers { + [TestFixture] + public class UrlRuleConditionTest { + private IContainer _container; + private IConditionProvider _urlCondition; + private StubHttpContextAccessor _stubContextAccessor; + private ShellSettings _shellSettings; + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + _shellSettings = new ShellSettings { RequestUrlPrefix = String.Empty }; + builder.RegisterType().As(); + builder.RegisterInstance(_shellSettings); + _stubContextAccessor = new StubHttpContextAccessor(); + builder.RegisterInstance(_stubContextAccessor).As(); + _container = builder.Build(); + _urlCondition = _container.Resolve(); + } + + [Test] + public void UrlForHomePageMatchesHomePagePath() { + _stubContextAccessor.Set(new StubHttpContext("~/")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + + [Test] + public void UrlForAboutPageMatchesAboutPagePath() { + _stubContextAccessor.Set(new StubHttpContext("~/about")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/about" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + + [Test] + public void UrlForBlogWithEndingWildcardMatchesBlogPostPageInSaidBlog() { + _stubContextAccessor.Set(new StubHttpContext("~/my-blog/my-blog-post")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/my-blog/*" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + + [Test] + public void UrlForHomePageDoesNotMatchAboutPagePath() { + _stubContextAccessor.Set(new StubHttpContext("~/about")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.False); + } + + [Test] + public void UrlForAboutPageMatchesDifferentCasedAboutPagePath() { + _stubContextAccessor.Set(new StubHttpContext("~/About")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/about" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + + [Test] + public void UrlForAboutPageWithEndingSlashMatchesAboutPagePath() { + _stubContextAccessor.Set(new StubHttpContext("~/About/")); + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/about" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + + [Test] + public void UrlForHomePageMatchesHomePagePathWithUrlPrefix() { + _stubContextAccessor.Set(new StubHttpContext("~/site1")); + _shellSettings.RequestUrlPrefix = "site1"; + var context = new ConditionEvaluationContext { FunctionName = "url", Arguments = new[] { "~/" } }; + _urlCondition.Evaluate(context); + Assert.That(context.Result, Is.True); + } + } } \ No newline at end of file diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 103e102d1..3c30725bd 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -190,9 +190,9 @@ - + - + @@ -225,6 +225,10 @@ {14C049FD-B35B-415A-A824-87F26B26E7FD} Orchard.Comments + + {98251eae-a41b-47b2-aa91-e28b8482da70} + Orchard.Conditions + {4A4595EF-6C37-4F99-96ED-4AE0B9E438D3} Orchard.DesignerTools diff --git a/src/Orchard.Tests.Modules/Widgets/WidgetsTests.cs b/src/Orchard.Tests.Modules/Widgets/WidgetsTests.cs deleted file mode 100644 index 4b34a09df..000000000 --- a/src/Orchard.Tests.Modules/Widgets/WidgetsTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using Autofac; -using NUnit.Framework; -using Orchard.Scripting; -using Orchard.Caching; -using Orchard.Tests.Stubs; -using Orchard.Widgets.RuleEngine; -using Orchard.Widgets.Services; - -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().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - - _container = builder.Build(); - _ruleManager = _container.Resolve(); - } - - [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; - } - } -} - diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Module.txt b/src/Orchard.Web/Modules/Orchard.Conditions/Module.txt new file mode 100644 index 000000000..2c60ad94a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Module.txt @@ -0,0 +1,13 @@ +Name: Conditions +AntiForgery: enabled +Author: The Orchard Team +Website: http://orchardproject.net +Version: 1.0 +OrchardVersion: 1.9 +Description: Provides a rules API that evaluate to true or false. +Features: + Orchard.Conditions: + Name: Conditions + Description: Provides a rules API that evaluate to true or false. + Category: Scripting + Dependencies: Orchard.Scripting \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Orchard.Conditions.csproj b/src/Orchard.Web/Modules/Orchard.Conditions/Orchard.Conditions.csproj new file mode 100644 index 000000000..a243ac54b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Orchard.Conditions.csproj @@ -0,0 +1,144 @@ + + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {98251EAE-A41B-47B2-AA91-E28B8482DA70} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.Conditions + Orchard.Conditions + v4.5.1 + false + + + 4.0 + + + false + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + ..\..\..\OrchardBasicCorrectness.ruleset + false + + + pdbonly + true + bin\ + TRACE + prompt + 4 + AllRules.ruleset + false + + + + + + + 3.5 + + + + False + ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + false + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + false + + + {99002b65-86f7-415e-bf4a-381aa8ab9ccc} + Orchard.Scripting + + + + + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + $(ProjectDir)\..\Manifests + + + + + + + + + + + + False + True + 45979 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fb44458d9 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +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.Conditions")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Orchard")] +[assembly: AssemblyCopyright("")] +[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("1c288740-10c0-4fa9-a051-501d259e4fc0")] + +// 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.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Providers/AuthenticatedCondition.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/AuthenticatedCondition.cs new file mode 100644 index 000000000..cfbc6d4a4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/AuthenticatedCondition.cs @@ -0,0 +1,26 @@ +using System; +using Orchard.Conditions.Services; +using Orchard.Security; + +namespace Orchard.Conditions.Providers { + public class AuthenticatedCondition : IConditionProvider { + private readonly IAuthenticationService _authenticationService; + + public AuthenticatedCondition(IAuthenticationService authenticationService) { + _authenticationService = authenticationService; + } + + public void Evaluate(ConditionEvaluationContext evaluationContext) { + if (!String.Equals(evaluationContext.FunctionName, "authenticated", StringComparison.OrdinalIgnoreCase)) { + return; + } + + if (_authenticationService.GetAuthenticatedUser() != null) { + evaluationContext.Result = true; + return; + } + + evaluationContext.Result = false; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Providers/BuiltinCondition.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/BuiltinCondition.cs new file mode 100644 index 000000000..5d8436652 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/BuiltinCondition.cs @@ -0,0 +1,18 @@ +using System; +using Orchard.Conditions.Services; + +namespace Orchard.Conditions.Providers { + public class BuiltinCondition : IConditionProvider { + private readonly IWorkContextAccessor _workContextAccessor; + + public BuiltinCondition(IWorkContextAccessor workContextAccessor) { + _workContextAccessor = workContextAccessor; + } + + public void Evaluate(ConditionEvaluationContext evaluationContext) { + if (string.Equals(evaluationContext.FunctionName, "WorkContext", StringComparison.OrdinalIgnoreCase)) { + evaluationContext.Result = _workContextAccessor.GetContext(); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/UrlCondition.cs similarity index 72% rename from src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs rename to src/Orchard.Web/Modules/Orchard.Conditions/Providers/UrlCondition.cs index 76a6cec48..e861fc3f2 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/UrlRuleProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Providers/UrlCondition.cs @@ -1,47 +1,47 @@ -using System; -using System.Web; -using Orchard.Environment.Configuration; -using Orchard.Mvc; -using Orchard.Widgets.Services; - -namespace Orchard.Widgets.RuleEngine { - public class UrlRuleProvider : IRuleProvider { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ShellSettings _shellSettings; - - public UrlRuleProvider(IHttpContextAccessor httpContextAccessor, ShellSettings shellSettings) { - _httpContextAccessor = httpContextAccessor; - _shellSettings = shellSettings; - } - - public void Process(RuleContext ruleContext) { - if (!String.Equals(ruleContext.FunctionName, "url", StringComparison.OrdinalIgnoreCase)) - return; - - var context = _httpContextAccessor.Current(); - var url = Convert.ToString(ruleContext.Arguments[0]); - if (url.StartsWith("~/")) { - url = url.Substring(2); - var appPath = context.Request.ApplicationPath; - if (appPath == "/") - appPath = ""; - - if(!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) - appPath = String.Concat(appPath, "/", _shellSettings.RequestUrlPrefix); - - url = String.Concat(appPath, "/", url); - } - - if (!url.Contains("?")) - url = url.TrimEnd('/'); - - var requestPath = context.Request.Path; - if (!requestPath.Contains("?")) - requestPath = requestPath.TrimEnd('/'); - - ruleContext.Result = url.EndsWith("*") - ? requestPath.StartsWith(url.TrimEnd('*'), StringComparison.OrdinalIgnoreCase) - : string.Equals(requestPath, url, StringComparison.OrdinalIgnoreCase); - } - } +using System; +using System.Web; +using Orchard.Conditions.Services; +using Orchard.Environment.Configuration; +using Orchard.Mvc; + +namespace Orchard.Widgets.RuleEngine { + public class UrlCondition : IConditionProvider { + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly ShellSettings _shellSettings; + + public UrlCondition(IHttpContextAccessor httpContextAccessor, ShellSettings shellSettings) { + _httpContextAccessor = httpContextAccessor; + _shellSettings = shellSettings; + } + + public void Evaluate(ConditionEvaluationContext evaluationContext) { + if (!String.Equals(evaluationContext.FunctionName, "url", StringComparison.OrdinalIgnoreCase)) + return; + + var context = _httpContextAccessor.Current(); + var url = Convert.ToString(evaluationContext.Arguments[0]); + if (url.StartsWith("~/")) { + url = url.Substring(2); + var appPath = context.Request.ApplicationPath; + if (appPath == "/") + appPath = ""; + + if(!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) + appPath = String.Concat(appPath, "/", _shellSettings.RequestUrlPrefix); + + url = String.Concat(appPath, "/", url); + } + + if (!url.Contains("?")) + url = url.TrimEnd('/'); + + var requestPath = context.Request.Path; + if (!requestPath.Contains("?")) + requestPath = requestPath.TrimEnd('/'); + + evaluationContext.Result = url.EndsWith("*") + ? requestPath.StartsWith(url.TrimEnd('*'), StringComparison.OrdinalIgnoreCase) + : string.Equals(requestPath, url, StringComparison.OrdinalIgnoreCase); + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionEvaluationContext.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionEvaluationContext.cs new file mode 100644 index 000000000..fb69855a8 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionEvaluationContext.cs @@ -0,0 +1,7 @@ +namespace Orchard.Conditions.Services { + public class ConditionEvaluationContext { + public string FunctionName { get; set; } + public object[] Arguments { get; set; } + public object Result { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionManager.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionManager.cs new file mode 100644 index 000000000..e0738d238 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Services/ConditionManager.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using Orchard.Localization; +using Orchard.Scripting; + +namespace Orchard.Conditions.Services { + public class ConditionManager : IConditionManager { + private readonly IConditionProvider _conditions; + private readonly IEnumerable _evaluators; + + public ConditionManager(IConditionProvider conditions, IEnumerable evaluators) { + _conditions = conditions; + _evaluators = evaluators; + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + public bool Matches(string expression) { + var evaluator = _evaluators.FirstOrDefault(); + if (evaluator == null) { + throw new OrchardException(T("There are currently no scripting engines enabled")); + } + + var result = evaluator.Evaluate(expression, new List { new GlobalMethodProvider(this) }); + if (!(result is bool)) { + throw new OrchardException(T("Expression is not a boolean value")); + } + return (bool)result; + } + + private class GlobalMethodProvider : IGlobalMethodProvider { + private readonly ConditionManager _conditionManager; + + public GlobalMethodProvider(ConditionManager conditionManager) { + _conditionManager = conditionManager; + } + + public void Process(GlobalMethodContext context) { + var ruleContext = new ConditionEvaluationContext { + FunctionName = context.FunctionName, + Arguments = context.Arguments.ToArray(), + Result = context.Result + }; + + _conditionManager._conditions.Evaluate(ruleContext); + + context.Result = ruleContext.Result; + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionManager.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionManager.cs new file mode 100644 index 000000000..5d2482f46 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionManager.cs @@ -0,0 +1,5 @@ +namespace Orchard.Conditions.Services { + public interface IConditionManager : IDependency { + bool Matches(string expression); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionProvider.cs b/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionProvider.cs new file mode 100644 index 000000000..01e27fa3b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Services/IConditionProvider.cs @@ -0,0 +1,7 @@ +using Orchard.Events; + +namespace Orchard.Conditions.Services { + public interface IConditionProvider : IEventHandler { + void Evaluate(ConditionEvaluationContext evaluationContext); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Conditions/Web.config b/src/Orchard.Web/Modules/Orchard.Conditions/Web.config new file mode 100644 index 000000000..82da01f41 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Conditions/Web.config @@ -0,0 +1,43 @@ + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/ElementRuleCoordinator.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/ElementRuleCoordinator.cs index 05ace83fd..ad5fd9989 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/ElementRuleCoordinator.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/ElementRuleCoordinator.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; +using Orchard.Conditions.Services; using Orchard.Layouts.Framework.Display; using Orchard.Layouts.Services; -using Orchard.Widgets.Services; namespace Orchard.Layouts.Handlers { public class ElementRuleCoordinator : ElementEventHandlerBase { - private readonly IRuleManager _ruleManager; + private readonly IConditionManager _conditionManager; private readonly Dictionary _evaluations = new Dictionary(); - public ElementRuleCoordinator(IRuleManager ruleManager) { - _ruleManager = ruleManager; + public ElementRuleCoordinator(IConditionManager conditionManager) { + _conditionManager = conditionManager; } public override void CreatingDisplay(ElementCreatingDisplayShapeContext context) { @@ -27,7 +27,7 @@ namespace Orchard.Layouts.Handlers { if (_evaluations.ContainsKey(rule)) return _evaluations[rule]; - var result = _ruleManager.Matches(rule); + var result = _conditionManager.Matches(rule); _evaluations[rule] = result; return result; } diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt b/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt index 4f21508df..55e7dd8b7 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Module.txt @@ -10,7 +10,7 @@ Features: Orchard.Layouts: Name: Layouts Description: Provides tools to create layouts. - Dependencies: Common, Orchard.jQuery, Orchard.Forms, Orchard.Tokens, Orchard.MediaLibrary, Orchard.Widgets + Dependencies: Common, Orchard.jQuery, Orchard.Forms, Orchard.Tokens, Orchard.MediaLibrary, Orchard.Conditions Category: Layout Orchard.Layouts.Snippets: Name: Layout Snippets diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj index 39bf307c0..c379d3a19 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Orchard.Layouts.csproj @@ -86,7 +86,6 @@ - @@ -291,6 +290,10 @@ {66fccd76-2761-47e3-8d11-b45d0001ddaa} Orchard.Autoroute + + {98251eae-a41b-47b2-aa91-e28b8482da70} + Orchard.Conditions + {642a49d7-8752-4177-80d6-bfbbcfad3de0} Orchard.Forms @@ -312,10 +315,6 @@ {6f759635-13d7-4e94-bcc9-80445d63f117} Orchard.Tokens - - {194d3ccc-1153-474d-8176-fde8d7d0d0bd} - Orchard.Widgets - @@ -360,7 +359,6 @@ - @@ -607,12 +605,6 @@ - - - - - - diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Providers/PlaceableContentElementHarvester.cs b/src/Orchard.Web/Modules/Orchard.Layouts/Providers/PlaceableContentElementHarvester.cs index 62c49377f..3a7d37ab8 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Providers/PlaceableContentElementHarvester.cs +++ b/src/Orchard.Web/Modules/Orchard.Layouts/Providers/PlaceableContentElementHarvester.cs @@ -30,9 +30,7 @@ namespace Orchard.Layouts.Providers { return contentTypeDefinitions.Select(contentTypeDefinition => { var settings = contentTypeDefinition.Settings; var description = settings.ContainsKey("Description") ? settings["Description"] : contentTypeDefinition.DisplayName; - var stereotype = settings.ContainsKey("Stereotype") ? settings["Stereotype"] : default(string); - var category = GetCategoryFromStereotype(stereotype); - return new ElementDescriptor(typeof (PlaceableContentItem), contentTypeDefinition.Name, T(contentTypeDefinition.DisplayName), T(description), category) { + return new ElementDescriptor(typeof (PlaceableContentItem), contentTypeDefinition.Name, T(contentTypeDefinition.DisplayName), T(description), category: "Content Items") { Displaying = Displaying, Editor = Editor, UpdateEditor = UpdateEditor, @@ -112,18 +110,11 @@ namespace Orchard.Layouts.Providers { } var elementEditorShape = context.ShapeFactory.EditorTemplate(TemplateName: "Elements.PlaceableContentItem", Model: elementViewModel, Prefix: context.Prefix); - var editorWrapper = context.ShapeFactory.PlacedContentElementEditor(ContentItem: contentItem); - var stereotype = contentItem.TypeDefinition.Settings.ContainsKey("Stereotype") ? contentItem.TypeDefinition.Settings["Stereotype"] : default(string); - - if(!String.IsNullOrWhiteSpace(stereotype)) - editorWrapper.Metadata.Alternates.Add(String.Format("PlacedContentElementEditor__{0}", stereotype)); - - editorWrapper.Metadata.Position = "Properties:0"; + elementEditorShape.Metadata.Position = "Properties:0"; contentEditorShape.Metadata.Position = "Properties:0"; context.EditorResult.Add(elementEditorShape); context.EditorResult.Add(contentEditorShape); - context.EditorResult.Add(editorWrapper); } private void RemoveContentItem(ElementRemovingContext context) { @@ -171,23 +162,13 @@ namespace Orchard.Layouts.Providers { } private IEnumerable GetPlaceableContentTypeDefinitions() { - // Select all types that have either "Placeable" set ot true or the "Widget" stereotype. + // Select all types that have either "Placeable" set to true. var contentTypeDefinitionsQuery = from contentTypeDefinition in _contentManager.Value.GetContentTypeDefinitions() - let stereotype = contentTypeDefinition.Settings.ContainsKey("Stereotype") ? contentTypeDefinition.Settings["Stereotype"] : default(string) - where contentTypeDefinition.Settings.GetModel().Placeable || stereotype == "Widget" + where contentTypeDefinition.Settings.GetModel().Placeable select contentTypeDefinition; return contentTypeDefinitionsQuery.ToList(); } - - private string GetCategoryFromStereotype(string stereotype) { - switch (stereotype) { - case "Widget": - return "Widgets"; - default: - return "Content Items"; - } - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor-Widget.cshtml b/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor-Widget.cshtml deleted file mode 100644 index fb7ce67f8..000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor-Widget.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@{ - Script.Require("jQuery"); - Script.Include("placeable-widget-editor.js"); -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor.cshtml b/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor.cshtml deleted file mode 100644 index f96a12a9b..000000000 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Views/PlacedContentElementEditor.cshtml +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Conditions/ContentDisplayedRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Conditions/ContentDisplayedRuleProvider.cs new file mode 100644 index 000000000..ea53d8563 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Conditions/ContentDisplayedRuleProvider.cs @@ -0,0 +1,23 @@ +using System; +using Orchard.Conditions.Services; +using Orchard.Widgets.Handlers; + +namespace Orchard.Widgets.Conditions { + public class ContentDisplayedRuleProvider : IConditionProvider { + private readonly IDisplayedContentItemHandler _displayedContentItemHandler; + + public ContentDisplayedRuleProvider(IDisplayedContentItemHandler displayedContentItemHandler) { + _displayedContentItemHandler = displayedContentItemHandler; + } + + public void Evaluate(ConditionEvaluationContext evaluationContext) { + if (!String.Equals(evaluationContext.FunctionName, "contenttype", StringComparison.OrdinalIgnoreCase)) { + return; + } + + var contentType = Convert.ToString(evaluationContext.Arguments[0]); + + evaluationContext.Result = _displayedContentItemHandler.IsDisplayed(contentType); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Drivers/LayerPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Drivers/LayerPartDriver.cs index 202ea2692..c0cc0c2a7 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Drivers/LayerPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Drivers/LayerPartDriver.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Orchard.Conditions.Services; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; @@ -11,14 +12,14 @@ using Orchard.Widgets.Services; namespace Orchard.Widgets.Drivers { public class LayerPartDriver : ContentPartDriver { - private readonly IRuleManager _ruleManager; + private readonly IConditionManager _conditionManager; private readonly IWidgetsService _widgetsService; public LayerPartDriver( - IRuleManager ruleManager, + IConditionManager conditionManager, IWidgetsService widgetsService) { - _ruleManager = ruleManager; + _conditionManager = conditionManager; _widgetsService = widgetsService; T = NullLocalizer.Instance; @@ -53,7 +54,7 @@ namespace Orchard.Widgets.Drivers { } try { - _ruleManager.Matches(layerPart.LayerRule); + _conditionManager.Matches(layerPart.LayerRule); } catch (Exception e) { updater.AddModelError("Description", T("The rule is not valid: {0}", e.Message)); diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/WidgetPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Handlers/WidgetElementsHandler.cs similarity index 82% rename from src/Orchard.Web/Modules/Orchard.Layouts/Handlers/WidgetPartHandler.cs rename to src/Orchard.Web/Modules/Orchard.Widgets/Handlers/WidgetElementsHandler.cs index 90d464aa8..92d6ed4b1 100644 --- a/src/Orchard.Web/Modules/Orchard.Layouts/Handlers/WidgetPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Handlers/WidgetElementsHandler.cs @@ -1,15 +1,16 @@ using Orchard.ContentManagement; using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Handlers; +using Orchard.Environment.Extensions; using Orchard.Layouts.Helpers; -using Orchard.Layouts.Models; using Orchard.Widgets.Models; -namespace Orchard.Layouts.Handlers { - public class WidgetPartHandler : ContentHandler { +namespace Orchard.Widgets.Handlers { + [OrchardFeature("Orchard.Widgets.Elements")] + public class WidgetElementsHandler : ContentHandler { private readonly IOrchardServices _orchardServices; - public WidgetPartHandler(IOrchardServices orchardServices) { + public WidgetElementsHandler(IOrchardServices orchardServices) { _orchardServices = orchardServices; OnUpdated(PostProcessPlacedWidget); } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Elements/Widget.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Elements/Widget.cs new file mode 100644 index 000000000..ada221f17 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Elements/Widget.cs @@ -0,0 +1,25 @@ +using Orchard.Environment.Extensions; +using Orchard.Layouts.Framework.Elements; +using Orchard.Layouts.Helpers; + +namespace Orchard.Widgets.Layouts.Elements { + [OrchardFeature("Orchard.Widgets.Elements")] + public class Widget : Element { + public override string Category { + get { return "Widgets"; } + } + + public override bool IsSystemElement { + get { return true; } + } + + public override bool HasEditor { + get { return false; } + } + + public int? WidgetId { + get { return this.Retrieve(x => x.WidgetId); } + set { this.Store(x => x.WidgetId, value); } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Providers/WidgetElementHarvester.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Providers/WidgetElementHarvester.cs new file mode 100644 index 000000000..b5578837a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Layouts/Providers/WidgetElementHarvester.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Aspects; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.Core.Contents.Settings; +using Orchard.Environment; +using Orchard.Environment.Extensions; +using Orchard.Layouts.Framework.Display; +using Orchard.Layouts.Framework.Drivers; +using Orchard.Layouts.Framework.Elements; +using Orchard.Layouts.Framework.Harvesters; +using Orchard.Layouts.Helpers; +using Orchard.Widgets.Layouts.Elements; +using Orchard.Widgets.ViewModels; +using ContentItem = Orchard.ContentManagement.ContentItem; + +namespace Orchard.Widgets.Layouts.Providers { + [OrchardFeature("Orchard.Widgets.Elements")] + public class WidgetElementHarvester : Component, IElementHarvester { + private readonly Work _contentManager; + + public WidgetElementHarvester(Work contentManager) { + _contentManager = contentManager; + } + + public IEnumerable HarvestElements(HarvestElementsContext context) { + var contentTypeDefinitions = GetWidgetContentTypeDefinitions(); + + return contentTypeDefinitions.Select(contentTypeDefinition => { + var settings = contentTypeDefinition.Settings; + var description = settings.ContainsKey("Description") ? settings["Description"] : contentTypeDefinition.DisplayName; + return new ElementDescriptor(typeof (Widget), contentTypeDefinition.Name, T(contentTypeDefinition.DisplayName), T(description), category: "Widgets") { + Displaying = Displaying, + Editor = Editor, + UpdateEditor = UpdateEditor, + ToolboxIcon = "\uf1b2", + EnableEditorDialog = true, + Removing = RemoveContentItem, + Exporting = ExportElement, + Importing = ImportElement, + StateBag = new Dictionary { + { "ContentTypeName", contentTypeDefinition.Name } + } + }; + }); + } + + private void Displaying(ElementDisplayingContext context) { + var contentTypeName = (string)context.Element.Descriptor.StateBag["ContentTypeName"]; + var element = (Widget)context.Element; + var widgetId = element.WidgetId; + var versionOptions = context.DisplayType == "Design" ? VersionOptions.Latest : VersionOptions.Published; + var widget = widgetId != null + ? _contentManager.Value.Get(widgetId.Value, versionOptions) + : _contentManager.Value.New(contentTypeName); + + var widgetShape = widget != null ? _contentManager.Value.BuildDisplay(widget) : default(dynamic); + context.ElementShape.Widget = widget; + context.ElementShape.WidgetShape = widgetShape; + } + + private void Editor(ElementEditorContext context) { + UpdateEditor(context); + } + + private void UpdateEditor(ElementEditorContext context) { + var contentTypeName = (string)context.Element.Descriptor.StateBag["ContentTypeName"]; + var element = (Widget) context.Element; + var elementViewModel = new WidgetElementViewModel { + WidgetId = element.WidgetId + }; + + if (context.Updater != null) { + context.Updater.TryUpdateModel(elementViewModel, context.Prefix, null, null); + } + + var widgetId = elementViewModel.WidgetId; + var widget = widgetId != null + ? _contentManager.Value.Get(widgetId.Value, VersionOptions.Latest) + : _contentManager.Value.New(contentTypeName); + + dynamic contentEditorShape; + + if (context.Updater != null) { + if (widget.Id == 0) { + _contentManager.Value.Create(widget, VersionOptions.Draft); + } + else { + var isDraftable = widget.TypeDefinition.Settings.GetModel().Draftable; + var versionOptions = isDraftable ? VersionOptions.DraftRequired : VersionOptions.Latest; + widget = _contentManager.Value.Get(widget.Id, versionOptions); + } + + element.WidgetId = widget.Id; + + // If the widget has the CommonPart attached, set its Container property to the Content (if any). + // This helps preventing widgets from appearing as orphans. + var commonPart = widget.As(); + if (commonPart != null) + commonPart.Container = context.Content; + + widget.IsPlaceableContent(true); + contentEditorShape = _contentManager.Value.UpdateEditor(widget, context.Updater); + + _contentManager.Value.Publish(widget); + } + else { + contentEditorShape = _contentManager.Value.BuildEditor(widget); + } + + var elementEditorShape = context.ShapeFactory.EditorTemplate(TemplateName: "Elements.Widget", Model: elementViewModel, Prefix: context.Prefix); + + elementEditorShape.Metadata.Position = "Properties:0"; + contentEditorShape.Metadata.Position = "Properties:0"; + context.EditorResult.Add(elementEditorShape); + context.EditorResult.Add(contentEditorShape); + } + + private void RemoveContentItem(ElementRemovingContext context) { + var element = (Widget) context.Element; + var widgetId = element.WidgetId; + + // Only remove the widget if no other elements are referencing this one. + // This can happen if the user cut an element and then pasted it back. + // That will delete the initial element and create a copy. + var widgetElements = + from e in context.Elements.Flatten() + let p = e as Widget + where p != null && p.WidgetId == widgetId + select p; + + if (widgetElements.Any()) + return; + + var contentItem = widgetId != null ? _contentManager.Value.Get(widgetId.Value, VersionOptions.Latest) : default(ContentItem); + + if(contentItem != null) + _contentManager.Value.Remove(contentItem); + } + + private void ExportElement(ExportElementContext context) { + var element = (Widget)context.Element; + var widgetId = element.WidgetId; + var widget = widgetId != null ? _contentManager.Value.Get(widgetId.Value, VersionOptions.Latest) : default(ContentItem); + var widgetIdentity = widget != null ? _contentManager.Value.GetItemMetadata(widget).Identity.ToString() : default(string); + + if (widgetIdentity != null) + context.ExportableData["WidgetId"] = widgetIdentity; + } + + private void ImportElement(ImportElementContext context) { + var widgetIdentity = context.ExportableData.Get("WidgetId"); + + if (String.IsNullOrWhiteSpace(widgetIdentity)) + return; + + var widget = context.Session.GetItemFromSession(widgetIdentity); + var element = (Widget)context.Element; + + element.WidgetId = widget != null ? widget.Id : default(int?); + } + + private IEnumerable GetWidgetContentTypeDefinitions() { + // Select all types that have either "the "Widget" stereotype. + var contentTypeDefinitionsQuery = + from contentTypeDefinition in _contentManager.Value.GetContentTypeDefinitions() + let stereotype = contentTypeDefinition.Settings.ContainsKey("Stereotype") ? contentTypeDefinition.Settings["Stereotype"] : default(string) + where stereotype == "Widget" + select contentTypeDefinition; + + return contentTypeDefinitionsQuery.ToList(); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Module.txt b/src/Orchard.Web/Modules/Orchard.Widgets/Module.txt index 1a0eb5338..3a8da5e14 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Module.txt @@ -7,7 +7,7 @@ OrchardVersion: 1.9 Description: An implementation of widgets for Orchard. FeatureDescription: An implementation of widgets. Category: Widget -Dependencies: Orchard.Scripting, Orchard.Themes +Dependencies: Orchard.Conditions, Orchard.Scripting, Orchard.Themes Features: Orchard.Widgets.PageLayerHinting: Name: Page Layer Hinting @@ -19,3 +19,8 @@ Features: Description: Add an Edit button on the front-end for authenticated users Category: Widget Dependencies: Orchard.Widgets + Orchard.Widgets.Elements + Name: Widget Elements + Description: Enables widgets to be added as elements to layouts. + Category: Layout + Dependencies: Orchard.Layouts \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj index d1aaa153a..a4c7a2837 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Orchard.Widgets.csproj @@ -26,6 +26,7 @@ + true @@ -74,6 +75,9 @@ + + + @@ -85,11 +89,8 @@ - - - - - + + @@ -99,12 +100,14 @@ + + @@ -131,6 +134,14 @@ Orchard.Framework True + + {98251eae-a41b-47b2-aa91-e28b8482da70} + Orchard.Conditions + + + {6bd8b2fa-f2e3-4ac8-a4c3-2925a653889a} + Orchard.Layouts + {99002B65-86F7-415E-BF4A-381AA8AB9CCC} Orchard.Scripting @@ -192,6 +203,20 @@ + + + + + + + + + + + + Designer + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/AuthenticatedRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/AuthenticatedRuleProvider.cs deleted file mode 100644 index 085f37bbf..000000000 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/AuthenticatedRuleProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Orchard.Security; -using Orchard.Widgets.Services; - -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; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/BuiltinRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/BuiltinRuleProvider.cs deleted file mode 100644 index c91ad04b3..000000000 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/BuiltinRuleProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Orchard.Widgets.Services; - -namespace Orchard.Widgets.RuleEngine { - public class BuiltinRuleProvider : IRuleProvider { - private readonly IWorkContextAccessor _workContextAccessor; - - public BuiltinRuleProvider(IWorkContextAccessor workContextAccessor) { - _workContextAccessor = workContextAccessor; - } - - public void Process(RuleContext ruleContext) { - if (string.Equals(ruleContext.FunctionName, "WorkContext", StringComparison.OrdinalIgnoreCase)) { - ruleContext.Result = _workContextAccessor.GetContext(); - } - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/ContentDisplayedRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/ContentDisplayedRuleProvider.cs deleted file mode 100644 index ee79b97de..000000000 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/ContentDisplayedRuleProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Orchard.Widgets.Handlers; -using Orchard.Widgets.Services; - -namespace Orchard.Widgets.RuleEngine { - public class ContentDisplayedRuleProvider : IRuleProvider { - private readonly IDisplayedContentItemHandler _displayedContentItemHandler; - - public ContentDisplayedRuleProvider(IDisplayedContentItemHandler displayedContentItemHandler) { - _displayedContentItemHandler = displayedContentItemHandler; - } - - public void Process(RuleContext ruleContext) { - if (!String.Equals(ruleContext.FunctionName, "contenttype", StringComparison.OrdinalIgnoreCase)) { - return; - } - - var contentType = Convert.ToString(ruleContext.Arguments[0]); - - ruleContext.Result = _displayedContentItemHandler.IsDisplayed(contentType); - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Scripts/Web.config b/src/Orchard.Web/Modules/Orchard.Widgets/Scripts/Web.config new file mode 100644 index 000000000..11135c337 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Scripts/Web.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.Layouts/Scripts/placeable-widget-editor.js b/src/Orchard.Web/Modules/Orchard.Widgets/Scripts/widget-element-editor.js similarity index 100% rename from src/Orchard.Web/Modules/Orchard.Layouts/Scripts/placeable-widget-editor.js rename to src/Orchard.Web/Modules/Orchard.Widgets/Scripts/widget-element-editor.js diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Services/DefaultLayerEvaluationService.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/DefaultLayerEvaluationService.cs index 0ad3908f8..55b4a0682 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Services/DefaultLayerEvaluationService.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/DefaultLayerEvaluationService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Orchard.Conditions.Services; using Orchard.Localization; using Orchard.Logging; using Orchard.Widgets.Models; @@ -8,13 +9,13 @@ using Orchard.ContentManagement.Utilities; namespace Orchard.Widgets.Services{ public class DefaultLayerEvaluationService : ILayerEvaluationService { - private readonly IRuleManager _ruleManager; + private readonly IConditionManager _conditionManager; private readonly IOrchardServices _orchardServices; private readonly LazyField _activeLayerIDs; - public DefaultLayerEvaluationService(IRuleManager ruleManager, IOrchardServices orchardServices) { - _ruleManager = ruleManager; + public DefaultLayerEvaluationService(IConditionManager conditionManager, IOrchardServices orchardServices) { + _conditionManager = conditionManager; _orchardServices = orchardServices; Logger = NullLogger.Instance; @@ -38,7 +39,7 @@ namespace Orchard.Widgets.Services{ } private int[] PopulateActiveLayers() { - // Once the Rule Engine is done: + // Once the Condition Engine is done: // Get Layers and filter by zone and rule // NOTE: .ForType("Layer") is faster than .Query() var activeLayers = _orchardServices.ContentManager.Query().WithQueryHints(new QueryHints().ExpandParts()).ForType("Layer").List(); @@ -47,7 +48,7 @@ namespace Orchard.Widgets.Services{ foreach (var activeLayer in activeLayers) { // ignore the rule if it fails to execute try { - if (_ruleManager.Matches(activeLayer.LayerRule)) { + if (_conditionManager.Matches(activeLayer.LayerRule)) { activeLayerIds.Add(activeLayer.ContentItem.Id); } } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleManager.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleManager.cs index 38cdae2da..e0e9feafd 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleManager.cs @@ -1,4 +1,7 @@ -namespace Orchard.Widgets.Services { +using System; + +namespace Orchard.Widgets.Services { + [Obsolete("Use Orchard.Conditions.Services.IConditionManager instead.")] public interface IRuleManager : IDependency { bool Matches(string expression); } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleProvider.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleProvider.cs index cd30e3f8c..4b5f87f2e 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/IRuleProvider.cs @@ -1,6 +1,8 @@ -using Orchard.Events; +using System; +using Orchard.Events; namespace Orchard.Widgets.Services { + [Obsolete("Use Orchard.Conditions.Services.IConditionProvider instead.")] public interface IRuleProvider : IEventHandler { void Process(RuleContext ruleContext); } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleContext.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleContext.cs index d1db6dea2..0d29400b9 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleContext.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleContext.cs @@ -1,4 +1,7 @@ -namespace Orchard.Widgets.Services { +using System; + +namespace Orchard.Widgets.Services { + [Obsolete("Use Orchard.Conditions.Services.ConditionEvaluationContext instead.")] public class RuleContext { public string FunctionName { get; set; } public object[] Arguments { get; set; } diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/RuleManager.cs b/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleManager.cs similarity index 91% rename from src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/RuleManager.cs rename to src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleManager.cs index cba7bc5e6..927164b72 100644 --- a/src/Orchard.Web/Modules/Orchard.Widgets/RuleEngine/RuleManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Services/RuleManager.cs @@ -1,53 +1,54 @@ -using System.Collections.Generic; -using System.Linq; -using Orchard.Localization; -using Orchard.Scripting; -using Orchard.Widgets.Services; - -namespace Orchard.Widgets.RuleEngine { - public class RuleManager : IRuleManager { - private readonly IRuleProvider _ruleProviders; - private readonly IEnumerable _evaluators; - - public RuleManager(IRuleProvider ruleProviders, IEnumerable evaluators) { - _ruleProviders = ruleProviders; - _evaluators = evaluators; - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - public bool Matches(string expression) { - var evaluator = _evaluators.FirstOrDefault(); - if (evaluator == null) { - throw new OrchardException(T("There are currently no scripting engines enabled")); - } - - var result = evaluator.Evaluate(expression, new List { new GlobalMethodProvider(this) }); - if (!(result is bool)) { - throw new OrchardException(T("Expression is not a boolean value")); - } - return (bool)result; - } - - private class GlobalMethodProvider : IGlobalMethodProvider { - private readonly RuleManager _ruleManager; - - public GlobalMethodProvider(RuleManager ruleManager) { - _ruleManager = ruleManager; - } - - public void Process(GlobalMethodContext context) { - var ruleContext = new RuleContext { - FunctionName = context.FunctionName, - Arguments = context.Arguments.ToArray(), - Result = context.Result - }; - - _ruleManager._ruleProviders.Process(ruleContext); - - context.Result = ruleContext.Result; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.Localization; +using Orchard.Scripting; + +namespace Orchard.Widgets.Services { + [Obsolete("Use Orchard.Conditions.Services.ConditionManager instead.")] + public class RuleManager : IRuleManager { + private readonly IRuleProvider _ruleProviders; + private readonly IEnumerable _evaluators; + + public RuleManager(IRuleProvider ruleProviders, IEnumerable evaluators) { + _ruleProviders = ruleProviders; + _evaluators = evaluators; + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + public bool Matches(string expression) { + var evaluator = _evaluators.FirstOrDefault(); + if (evaluator == null) { + throw new OrchardException(T("There are currently no scripting engines enabled")); + } + + var result = evaluator.Evaluate(expression, new List { new GlobalMethodProvider(this) }); + if (!(result is bool)) { + throw new OrchardException(T("Expression is not a boolean value")); + } + return (bool)result; + } + + private class GlobalMethodProvider : IGlobalMethodProvider { + private readonly RuleManager _ruleManager; + + public GlobalMethodProvider(RuleManager ruleManager) { + _ruleManager = ruleManager; + } + + public void Process(GlobalMethodContext context) { + var ruleContext = new RuleContext { + FunctionName = context.FunctionName, + Arguments = context.Arguments.ToArray(), + Result = context.Result + }; + + _ruleManager._ruleProviders.Process(ruleContext); + + context.Result = ruleContext.Result; + } + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/ViewModels/WidgetElementViewModel.cs b/src/Orchard.Web/Modules/Orchard.Widgets/ViewModels/WidgetElementViewModel.cs new file mode 100644 index 000000000..74801cd64 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/ViewModels/WidgetElementViewModel.cs @@ -0,0 +1,5 @@ +namespace Orchard.Widgets.ViewModels { + public class WidgetElementViewModel { + public int? WidgetId { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Views/EditorTemplates/Elements.Widget.cshtml b/src/Orchard.Web/Modules/Orchard.Widgets/Views/EditorTemplates/Elements.Widget.cshtml new file mode 100644 index 000000000..8f25d7b64 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Views/EditorTemplates/Elements.Widget.cshtml @@ -0,0 +1,6 @@ +@model Orchard.Widgets.ViewModels.WidgetElementViewModel +@{ + Script.Require("jQuery"); + Script.Include("widget-element-editor.js"); +} +@Html.HiddenFor(m => m.WidgetId) \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.Design.cshtml b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.Design.cshtml new file mode 100644 index 000000000..f109e9b73 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.Design.cshtml @@ -0,0 +1,12 @@ +@using Orchard.ContentManagement +@{ + var widget = (ContentItem)Model.Widget; +} +
+ @if (widget != null) { + @String.Format("{0} - {1}", Html.ItemDisplayText(widget), widget.TypeDefinition.DisplayName) + } + else { + @T("Widget Not Found"); + } +
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.cshtml b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.cshtml new file mode 100644 index 000000000..d6ef13757 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Widgets/Views/Elements/Widget.cshtml @@ -0,0 +1,9 @@ +@using Orchard.Layouts.Helpers +@{ + var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model); +} +@tagBuilder.StartElement +@if (Model.WidgetShape != null) { + @Display(Model.WidgetShape) +} +@tagBuilder.EndElement \ No newline at end of file diff --git a/src/Orchard.sln b/src/Orchard.sln index eff650fa4..6a8bed76d 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -270,6 +270,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gulp", "Gulp", "{90EBEE36-B Package.json = Package.json EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Conditions", "Orchard.Web\Modules\Orchard.Conditions\Orchard.Conditions.csproj", "{98251EAE-A41B-47B2-AA91-E28B8482DA70}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -1082,6 +1084,13 @@ Global {BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0}.FxCop|Any CPU.Build.0 = Release|Any CPU {BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0}.Release|Any CPU.Build.0 = Release|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.Coverage|Any CPU.ActiveCfg = Release|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.FxCop|Any CPU.ActiveCfg = Release|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98251EAE-A41B-47B2-AA91-E28B8482DA70}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1167,5 +1176,6 @@ Global {82190F52-2901-46D6-8A4C-34649959483F} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {BAC82DB9-F4C4-4DD1-ABDB-F70E6229E6B0} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {90EBEE36-B5CD-42A8-A21B-76270E2C5D24} = {DF3909B0-1DDD-4D8A-9919-56FC438E25E2} + {98251EAE-A41B-47B2-AA91-E28B8482DA70} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} EndGlobalSection EndGlobal