Layouts, Widgets and Conditions.

- Moved Rules engine code to new Orchard.Conditions module.
- Refactored Layouts and Widgets to use Conditions.
- Moved Widgets element harvester to the Widgets module as a separate feature.
This commit is contained in:
Sipke Schoorstra
2015-11-19 14:02:12 +01:00
parent 3b4b3c578a
commit 5c18f49fa3
43 changed files with 959 additions and 379 deletions

View File

@@ -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<ScriptExpressionEvaluator>().As<IScriptExpressionEvaluator>();
builder.RegisterType<AlwaysTrueCondition>().As<IConditionProvider>();
builder.RegisterType<ConditionManager>().As<IConditionManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
_container = builder.Build();
_conditionManager = _container.Resolve<IConditionManager>();
}
[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;
}
}
}

View File

@@ -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<UrlRuleProvider>().As<IRuleProvider>();
builder.RegisterInstance(_shellSettings);
_stubContextAccessor = new StubHttpContextAccessor();
builder.RegisterInstance(_stubContextAccessor).As<IHttpContextAccessor>();
_container = builder.Build();
_urlRuleProvider = _container.Resolve<IRuleProvider>();
}
[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<UrlCondition>().As<IConditionProvider>();
builder.RegisterInstance(_shellSettings);
_stubContextAccessor = new StubHttpContextAccessor();
builder.RegisterInstance(_stubContextAccessor).As<IHttpContextAccessor>();
_container = builder.Build();
_urlCondition = _container.Resolve<IConditionProvider>();
}
[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);
}
}
}

View File

@@ -190,9 +190,9 @@
<Compile Include="Users\Services\MembershipServiceTests.cs" />
<Compile Include="Warmup\WebDownloaderTests.cs" />
<Compile Include="Warmup\WarmupUpdaterTests.cs" />
<Compile Include="Widgets\RuleEngine\UrlRuleProviderTest.cs" />
<Compile Include="Conditions\Providers\UrlRuleConditionTest.cs" />
<Compile Include="Widgets\Services\WidgetsServiceTest.cs" />
<Compile Include="Widgets\WidgetsTests.cs" />
<Compile Include="Conditions\ConditionEvaluationTests.cs" />
<Compile Include="Workflows\Activities\WebRequestActivityTests.cs" />
<Compile Include="XmlRpc\Controllers\LiveWriterControllerTests.cs" />
<Compile Include="XmlRpc\Controllers\HomeControllerTests.cs" />
@@ -225,6 +225,10 @@
<Project>{14C049FD-B35B-415A-A824-87F26B26E7FD}</Project>
<Name>Orchard.Comments</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.Conditions\Orchard.Conditions.csproj">
<Project>{98251eae-a41b-47b2-aa91-e28b8482da70}</Project>
<Name>Orchard.Conditions</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Web\Modules\Orchard.DesignerTools\Orchard.DesignerTools.csproj">
<Project>{4A4595EF-6C37-4F99-96ED-4AE0B9E438D3}</Project>
<Name>Orchard.DesignerTools</Name>

View File

@@ -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<ScriptExpressionEvaluator>().As<IScriptExpressionEvaluator>();
builder.RegisterType<AlwaysTrueRuleProvider>().As<IRuleProvider>();
builder.RegisterType<RuleManager>().As<IRuleManager>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
_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;
}
}
}

View File

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

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.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>{98251EAE-A41B-47B2-AA91-E28B8482DA70}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Conditions</RootNamespace>
<AssemblyName>Orchard.Conditions</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>4.0</OldToolsVersion>
<UpgradeBackupLocation />
<TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
</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>..\..\..\OrchardBasicCorrectness.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</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>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<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=5.2.3, 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.Extensions" />
<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="Web.config" />
<Content Include="Properties\AssemblyInfo.cs" />
<Content Include="Module.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
<Name>Orchard.Framework</Name>
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
<Name>Orchard.Core</Name>
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.Scripting\Orchard.Scripting.csproj">
<Project>{99002b65-86f7-415e-bf4a-381aa8ab9ccc}</Project>
<Name>Orchard.Scripting</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Providers\AuthenticatedCondition.cs" />
<Compile Include="Providers\BuiltinCondition.cs" />
<Compile Include="Services\ConditionManager.cs" />
<Compile Include="Providers\UrlCondition.cs" />
<Compile Include="Services\IConditionManager.cs" />
<Compile Include="Services\IConditionProvider.cs" />
<Compile Include="Services\ConditionEvaluationContext.cs" />
</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,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")]

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<IScriptExpressionEvaluator> _evaluators;
public ConditionManager(IConditionProvider conditions, IEnumerable<IScriptExpressionEvaluator> 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<IGlobalMethodProvider> { 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;
}
}
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Conditions.Services {
public interface IConditionManager : IDependency {
bool Matches(string expression);
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.Events;
namespace Orchard.Conditions.Services {
public interface IConditionProvider : IEventHandler {
void Evaluate(ConditionEvaluationContext evaluationContext);
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.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=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.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=5.2.3, 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"/>
</namespaces>
</pages>
</system.web.webPages.razor>
<system.web>
<compilation targetFramework="4.5.1">
<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=5.2.3, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add assembly="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add assembly="Orchard.Framework,Culture=neutral, PublicKeyToken=null"/>
<add assembly="Orchard.Core,Culture=neutral, PublicKeyToken=null"/>
</assemblies>
</compilation>
</system.web>
</configuration>

View File

@@ -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<string, bool> _evaluations = new Dictionary<string, bool>();
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;
}

View File

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

View File

@@ -86,7 +86,6 @@
<Content Include="Images\illustration.jpg" />
<Content Include="Images\logo.svg" />
<Content Include="Placement.info" />
<Content Include="Scripts\placeable-widget-editor.js" />
<Content Include="Scripts\element-browser.js" />
<Content Include="Scripts\AutoFocus.js" />
<Content Include="Scripts\Elements\Image.js" />
@@ -291,6 +290,10 @@
<Project>{66fccd76-2761-47e3-8d11-b45d0001ddaa}</Project>
<Name>Orchard.Autoroute</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Conditions\Orchard.Conditions.csproj">
<Project>{98251eae-a41b-47b2-aa91-e28b8482da70}</Project>
<Name>Orchard.Conditions</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Forms\Orchard.Forms.csproj">
<Project>{642a49d7-8752-4177-80d6-bfbbcfad3de0}</Project>
<Name>Orchard.Forms</Name>
@@ -312,10 +315,6 @@
<Project>{6f759635-13d7-4e94-bcc9-80445d63f117}</Project>
<Name>Orchard.Tokens</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Widgets\Orchard.Widgets.csproj">
<Project>{194d3ccc-1153-474d-8176-fde8d7d0d0bd}</Project>
<Name>Orchard.Widgets</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="AdminMenu.cs" />
@@ -360,7 +359,6 @@
<Compile Include="Framework\Elements\IElement.cs" />
<Compile Include="Handlers\ElementDriversCoordinator.cs" />
<Compile Include="Handlers\ElementRuleCoordinator.cs" />
<Compile Include="Handlers\WidgetPartHandler.cs" />
<Compile Include="Helpers\PlaceableContentExtensions.cs" />
<Compile Include="Helpers\DictionaryExtensions.cs" />
<Compile Include="Helpers\EditorResultExtensions.cs" />
@@ -607,12 +605,6 @@
<ItemGroup>
<Content Include="Views\DefinitionTemplates\ContentTypeLayoutSettings.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\PlacedContentElementEditor.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\PlacedContentElementEditor-Widget.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Elements.PlaceableContentItem.cshtml" />
</ItemGroup>

View File

@@ -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<ContentTypeDefinition> 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<ContentTypeLayoutSettings>().Placeable || stereotype == "Widget"
where contentTypeDefinition.Settings.GetModel<ContentTypeLayoutSettings>().Placeable
select contentTypeDefinition;
return contentTypeDefinitionsQuery.ToList();
}
private string GetCategoryFromStereotype(string stereotype) {
switch (stereotype) {
case "Widget":
return "Widgets";
default:
return "Content Items";
}
}
}
}

View File

@@ -1,4 +0,0 @@
@{
Script.Require("jQuery");
Script.Include("placeable-widget-editor.js");
}

View File

@@ -1 +0,0 @@
<!-- Placeholder -->

View File

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

View File

@@ -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<LayerPart> {
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));

View File

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

View File

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

View File

@@ -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<IContentManager> _contentManager;
public WidgetElementHarvester(Work<IContentManager> contentManager) {
_contentManager = contentManager;
}
public IEnumerable<ElementDescriptor> 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<string, object> {
{ "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<ContentTypeSettings>().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<ICommonPart>();
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<ContentTypeDefinition> 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();
}
}
}

View File

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

View File

@@ -26,6 +26,7 @@
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<TargetFrameworkProfile />
<UseGlobalApplicationHostFile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -74,6 +75,9 @@
<Compile Include="Handlers\DisplayedContentItemHandler.cs" />
<Compile Include="Handlers\LayerHintHandler.cs" />
<Compile Include="Drivers\WidgetPartDriver.cs" />
<Compile Include="Handlers\WidgetElementsHandler.cs" />
<Compile Include="Layouts\Elements\Widget.cs" />
<Compile Include="Layouts\Providers\WidgetElementHarvester.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Handlers\LayerPartHandler.cs" />
<Compile Include="Handlers\WidgetPartHandler.cs" />
@@ -85,11 +89,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Filters\WidgetFilter.cs" />
<Compile Include="ResourceManifest.cs" />
<Compile Include="RuleEngine\AuthenticatedRuleProvider.cs" />
<Compile Include="RuleEngine\BuiltinRuleProvider.cs" />
<Compile Include="RuleEngine\ContentDisplayedRuleProvider.cs" />
<Compile Include="RuleEngine\RuleManager.cs" />
<Compile Include="RuleEngine\UrlRuleProvider.cs" />
<Compile Include="Conditions\ContentDisplayedRuleProvider.cs" />
<Compile Include="Services\RuleManager.cs" />
<Compile Include="Services\DefaultLayerEvaluationService.cs" />
<Compile Include="Services\ILayerEvaluationService.cs" />
<Compile Include="Services\IRuleManager.cs" />
@@ -99,12 +100,14 @@
<Compile Include="Services\RuleContext.cs" />
<Compile Include="Services\WidgetsService.cs" />
<Compile Include="Shapes.cs" />
<Compile Include="ViewModels\WidgetElementViewModel.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Content\Admin\images\edit.png" />
<Content Include="Content\Admin\images\moveup.gif" />
<Content Include="Content\Admin\images\movedown.gif" />
<Content Include="Module.txt" />
<Content Include="Scripts\widget-element-editor.js" />
<Content Include="Styles\images\all-zones-on.gif" />
<Content Include="Styles\images\all-zones.gif" />
<Content Include="Styles\images\arrows.png" />
@@ -131,6 +134,14 @@
<Name>Orchard.Framework</Name>
<Private>True</Private>
</ProjectReference>
<ProjectReference Include="..\Orchard.Conditions\Orchard.Conditions.csproj">
<Project>{98251eae-a41b-47b2-aa91-e28b8482da70}</Project>
<Name>Orchard.Conditions</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Layouts\Orchard.Layouts.csproj">
<Project>{6bd8b2fa-f2e3-4ac8-a4c3-2925a653889a}</Project>
<Name>Orchard.Layouts</Name>
</ProjectReference>
<ProjectReference Include="..\Orchard.Scripting\Orchard.Scripting.csproj">
<Project>{99002B65-86F7-415E-BF4A-381AA8AB9CCC}</Project>
<Name>Orchard.Scripting</Name>
@@ -192,6 +203,20 @@
<ItemGroup>
<Content Include="Views\WidgetPlacement.Orphans.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Elements\Widget.Design.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Elements\Widget.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Elements.Widget.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Scripts\Web.config">
<SubType>Designer</SubType>
</Content>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<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

@@ -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<int[]> _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<LayerPart, LayerPartRecord>()
var activeLayers = _orchardServices.ContentManager.Query<LayerPart>().WithQueryHints(new QueryHints().ExpandParts<LayerPart>()).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);
}
}

View File

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

View File

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

View File

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

View File

@@ -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<IScriptExpressionEvaluator> _evaluators;
public RuleManager(IRuleProvider ruleProviders, IEnumerable<IScriptExpressionEvaluator> 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<IGlobalMethodProvider> { 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<IScriptExpressionEvaluator> _evaluators;
public RuleManager(IRuleProvider ruleProviders, IEnumerable<IScriptExpressionEvaluator> 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<IGlobalMethodProvider> { 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;
}
}
}
}

View File

@@ -0,0 +1,5 @@
namespace Orchard.Widgets.ViewModels {
public class WidgetElementViewModel {
public int? WidgetId { get; set; }
}
}

View File

@@ -0,0 +1,6 @@
@model Orchard.Widgets.ViewModels.WidgetElementViewModel
@{
Script.Require("jQuery");
Script.Include("widget-element-editor.js");
}
@Html.HiddenFor(m => m.WidgetId)

View File

@@ -0,0 +1,12 @@
@using Orchard.ContentManagement
@{
var widget = (ContentItem)Model.Widget;
}
<div class="layout-placeholder">
@if (widget != null) {
@String.Format("{0} - {1}", Html.ItemDisplayText(widget), widget.TypeDefinition.DisplayName)
}
else {
@T("Widget Not Found");
}
</div>

View File

@@ -0,0 +1,9 @@
@using Orchard.Layouts.Helpers
@{
var tagBuilder = TagBuilderExtensions.CreateElementTagBuilder(Model);
}
@tagBuilder.StartElement
@if (Model.WidgetShape != null) {
@Display(Model.WidgetShape)
}
@tagBuilder.EndElement

View File

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