diff --git a/.hgignore b/.hgignore index 704e42375..0e4e5c05c 100644 --- a/.hgignore +++ b/.hgignore @@ -12,3 +12,4 @@ glob:src/Orchard.Web/Modules/Orchard.DevTools/Module.txt glob:src/Orchard.Web/Modules/Orchard.Sandbox/Module.txt glob:src/Orchard.Web/Media/* glob:desktop.ini +glob:src/Orchard.Azure.suo diff --git a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs index 605692f79..0558fb854 100644 --- a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs +++ b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs @@ -47,23 +47,11 @@ namespace Orchard.Specs.Bindings { descriptor.EnabledFeatures.Concat(new[] { new ShellFeature { Name = name } }), descriptor.Parameters); } - - Trace.WriteLine("This call to Host.Reinitialize should not be needed, eventually"); - MvcApplication.Host.Reinitialize_Obsolete(); }); } - [When(@"I cycle the app domain")] - public void WhenICycleTheAppDomain() { - var webApp = Binding(); - webApp.Host.Execute(() => { - Trace.WriteLine("This call to Host.Reinitialize should not be needed, eventually"); - MvcApplication.Host.Reinitialize_Obsolete(); - }); - } - [Given(@"I have tenant ""(.*)\"" on ""(.*)\"" as ""(.*)\""")] public void GivenIHaveTenantOnSiteAsName(string shellName, string hostName, string siteName) { var webApp = Binding(); @@ -76,7 +64,6 @@ namespace Orchard.Specs.Bindings { using (var environment = MvcApplication.CreateStandaloneEnvironment("Default")) { environment.Resolve().SaveSettings(shellSettings); } - MvcApplication.Host.Reinitialize_Obsolete(); }); webApp.WhenIGoToPathOnHost("Setup", hostName); diff --git a/src/Orchard.Specs/Modules.feature b/src/Orchard.Specs/Modules.feature index 718784b3a..dcb4980fe 100644 --- a/src/Orchard.Specs/Modules.feature +++ b/src/Orchard.Specs/Modules.feature @@ -19,5 +19,6 @@ Scenario: Edit module shows its features Scenario: Features of installed modules are listed Given I have installed Orchard When I go to "admin/modules/features" - Then I should see "

Available Features

" + Then I should see "

Manage Features

" + And I should see "

Common

" And the status should be 200 OK \ No newline at end of file diff --git a/src/Orchard.Specs/Modules.feature.cs b/src/Orchard.Specs/Modules.feature.cs index 67ca158e7..57e342153 100644 --- a/src/Orchard.Specs/Modules.feature.cs +++ b/src/Orchard.Specs/Modules.feature.cs @@ -102,8 +102,10 @@ this.ScenarioSetup(scenarioInfo); #line 21 testRunner.When("I go to \"admin/modules/features\""); #line 22 - testRunner.Then("I should see \"

Available Features

\""); + testRunner.Then("I should see \"

Manage Features

\""); #line 23 + testRunner.And("I should see \"

Common

\""); +#line 24 testRunner.And("the status should be 200 OK"); #line hidden testRunner.CollectScenarioErrors(); diff --git a/src/Orchard.Specs/MultiTenancy.feature b/src/Orchard.Specs/MultiTenancy.feature index ed5bbed0b..b91693135 100644 --- a/src/Orchard.Specs/MultiTenancy.feature +++ b/src/Orchard.Specs/MultiTenancy.feature @@ -79,6 +79,6 @@ Scenario: Listing tenants from command line Given I have installed Orchard And I have installed "Orchard.MultiTenancy" And I have tenant "Alpha" on "example.org" as "New-site-name" - When I execute >orchard tenant list + When I execute >tenant list Then I should see "Name: Alpha" And I should see "Request Url Host: example.org" diff --git a/src/Orchard.Specs/MultiTenancy.feature.cs b/src/Orchard.Specs/MultiTenancy.feature.cs index 701a66ee0..ec7bf1d9f 100644 --- a/src/Orchard.Specs/MultiTenancy.feature.cs +++ b/src/Orchard.Specs/MultiTenancy.feature.cs @@ -8,7 +8,7 @@ // the code is regenerated. // // ------------------------------------------------------------------------------ -namespace Orchard.Specs +namespace Orchard.Tests { using TechTalk.SpecFlow; diff --git a/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs b/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs index ae058f009..17175465e 100644 --- a/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs +++ b/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs @@ -137,7 +137,7 @@ namespace Orchard.Tests.Modules.Settings.Topology { Enumerable.Empty(), Enumerable.Empty()); - Assert.That(eventBus.LastMessageName, Is.EqualTo(typeof(IShellDescriptorManager).FullName + ".UpdateShellDescriptor")); + Assert.That(eventBus.LastMessageName, Is.EqualTo("ShellDescriptor_Changed")); } } } diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs index 6bae8039a..d47d66904 100644 --- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs @@ -1,7 +1,9 @@ using System.IO; using System.Linq; +using Moq; using NUnit.Framework; using Orchard.Environment.Configuration; +using Orchard.Events; namespace Orchard.Tests.Environment.Configuration { [TestFixture] @@ -26,7 +28,7 @@ namespace Orchard.Tests.Environment.Configuration { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); - IShellSettingsManager loader = new ShellSettingsManager(_appData); + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var settings = loader.LoadSettings().Single(); Assert.That(settings, Is.Not.Null); Assert.That(settings.Name, Is.EqualTo("Default")); @@ -41,7 +43,7 @@ namespace Orchard.Tests.Environment.Configuration { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); _appData.CreateFile("Sites\\Another\\Settings.txt", "Name: Another\r\nDataProvider: SQLite2\r\nDataConnectionString: something else2"); - IShellSettingsManager loader = new ShellSettingsManager(_appData); + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var settings = loader.LoadSettings(); Assert.That(settings.Count(), Is.EqualTo(2)); @@ -59,8 +61,8 @@ namespace Orchard.Tests.Environment.Configuration { [Test] public void NewSettingsCanBeStored() { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); - - IShellSettingsManager loader = new ShellSettingsManager(_appData); + + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var foo = new ShellSettings {Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux"}; Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); diff --git a/src/Orchard.Tests/Mvc/Routes/ShellRouteTests.cs b/src/Orchard.Tests/Mvc/Routes/ShellRouteTests.cs index 886845203..dea1069c2 100644 --- a/src/Orchard.Tests/Mvc/Routes/ShellRouteTests.cs +++ b/src/Orchard.Tests/Mvc/Routes/ShellRouteTests.cs @@ -7,6 +7,7 @@ using System.Web.Mvc; using System.Web.Routing; using Autofac; using Autofac.Integration.Web; +using Moq; using NUnit.Framework; using Orchard.Environment; using Orchard.Environment.Configuration; @@ -87,15 +88,15 @@ namespace Orchard.Tests.Mvc.Routes { var routeC = new Route("quux", new MvcRouteHandler()); _containerA.Resolve().Publish( - new[] {new RouteDescriptor {Priority = 0, Route = routeA}}); + new[] { new RouteDescriptor { Priority = 0, Route = routeA } }); _containerB.Resolve().Publish( - new[] {new RouteDescriptor {Priority = 0, Route = routeB}}); + new[] { new RouteDescriptor { Priority = 0, Route = routeB } }); Assert.That(_routes.Count(), Is.EqualTo(2)); _containerA.Resolve().Publish( - new[] {new RouteDescriptor {Priority = 0, Route = routeC}}); + new[] { new RouteDescriptor { Priority = 0, Route = routeC } }); Assert.That(_routes.Count(), Is.EqualTo(2)); @@ -110,17 +111,17 @@ namespace Orchard.Tests.Mvc.Routes { [Test] public void MatchingRouteToActiveShellTableWillLimitTheAbilityToMatchRoutes() { - + var routeFoo = new Route("foo", new MvcRouteHandler()); _settingsA.RequestUrlHost = "a.example.com"; _containerA.Resolve().Publish( - new[] {new RouteDescriptor {Priority = 0, Route = routeFoo}}); + new[] { new RouteDescriptor { Priority = 0, Route = routeFoo } }); _rootContainer.Resolve().Add(_settingsA); _settingsB.RequestUrlHost = "b.example.com"; _containerB.Resolve().Publish( - new[] {new RouteDescriptor {Priority = 0, Route = routeFoo}}); + new[] { new RouteDescriptor { Priority = 0, Route = routeFoo } }); _rootContainer.Resolve().Add(_settingsB); var httpContext = new StubHttpContext("~/foo"); @@ -143,6 +144,86 @@ namespace Orchard.Tests.Mvc.Routes { Assert.That(routeContainerProviderB.ApplicationContainer.Resolve(), Is.SameAs(_containerB.Resolve())); Assert.That(routeContainerProviderB.ApplicationContainer.Resolve(), Is.Not.SameAs(_containerA.Resolve())); } - + + [Test] + public void RequestUrlPrefixAdjustsMatchingAndPathGeneration() { + var settings = new ShellSettings { RequestUrlPrefix = "~/foo" }; + + var builder = new ContainerBuilder(); + builder.RegisterType().InstancePerDependency(); + builder.RegisterAutoMocking(); + builder.Register(ctx => settings); + + var container = builder.Build(); + container.Mock() + .Setup(x => x.Match(It.IsAny())) + .Returns(settings); + + + var shellRouteFactory = container.Resolve>(); + + var helloRoute = shellRouteFactory(new Route( + "hello", + new RouteValueDictionary { { "controller", "foo" }, { "action", "bar" } }, + new MvcRouteHandler())); + + var tagsRoute = shellRouteFactory(new Route( + "tags/{tagName}", + new RouteValueDictionary { { "controller", "tags" }, { "action", "show" } }, + new MvcRouteHandler())); + + var defaultRoute = shellRouteFactory(new Route( + "{controller}/{action}", + new RouteValueDictionary { { "controller", "home" }, { "action", "index" } }, + new MvcRouteHandler())); + + var routes = new RouteCollection { helloRoute, tagsRoute, defaultRoute }; + + var helloRouteData = routes.GetRouteData(new StubHttpContext("~/Foo/Hello")); + Assert.That(helloRouteData, Is.Not.Null); + Assert.That(helloRouteData.Values.Count(), Is.EqualTo(2)); + Assert.That(helloRouteData.GetRequiredString("controller"), Is.EqualTo("foo")); + Assert.That(helloRouteData.GetRequiredString("action"), Is.EqualTo("bar")); + + var tagsRouteData = routes.GetRouteData(new StubHttpContext("~/Foo/Tags/my-tag-name")); + Assert.That(tagsRouteData, Is.Not.Null); + Assert.That(tagsRouteData.Values.Count(), Is.EqualTo(3)); + Assert.That(tagsRouteData.GetRequiredString("controller"), Is.EqualTo("tags")); + Assert.That(tagsRouteData.GetRequiredString("action"), Is.EqualTo("show")); + Assert.That(tagsRouteData.GetRequiredString("tagName"), Is.EqualTo("my-tag-name")); + + var defaultRouteData = routes.GetRouteData(new StubHttpContext("~/Foo/Alpha/Beta")); + Assert.That(defaultRouteData, Is.Not.Null); + Assert.That(defaultRouteData.Values.Count(), Is.EqualTo(2)); + Assert.That(defaultRouteData.GetRequiredString("controller"), Is.EqualTo("Alpha")); + Assert.That(defaultRouteData.GetRequiredString("action"), Is.EqualTo("Beta")); + + var defaultRouteData2 = routes.GetRouteData(new StubHttpContext("~/Foo/Alpha")); + Assert.That(defaultRouteData2, Is.Not.Null); + Assert.That(defaultRouteData2.Values.Count(), Is.EqualTo(2)); + Assert.That(defaultRouteData2.GetRequiredString("controller"), Is.EqualTo("Alpha")); + Assert.That(defaultRouteData2.GetRequiredString("action"), Is.EqualTo("index")); + + var defaultRouteData3 = routes.GetRouteData(new StubHttpContext("~/Foo/")); + Assert.That(defaultRouteData3, Is.Not.Null); + Assert.That(defaultRouteData3.Values.Count(), Is.EqualTo(2)); + Assert.That(defaultRouteData3.GetRequiredString("controller"), Is.EqualTo("home")); + Assert.That(defaultRouteData3.GetRequiredString("action"), Is.EqualTo("index")); + + var defaultRouteData4 = routes.GetRouteData(new StubHttpContext("~/Foo")); + Assert.That(defaultRouteData4, Is.Not.Null); + Assert.That(defaultRouteData4.Values.Count(), Is.EqualTo(2)); + Assert.That(defaultRouteData4.GetRequiredString("controller"), Is.EqualTo("home")); + Assert.That(defaultRouteData4.GetRequiredString("action"), Is.EqualTo("index")); + + var requestContext = new RequestContext(new StubHttpContext("~/Foo/Alpha/Beta"), defaultRouteData); + var helloVirtualPath = routes.GetVirtualPath(requestContext, helloRouteData.Values); + Assert.That(helloVirtualPath, Is.Not.Null); + Assert.That(helloVirtualPath.VirtualPath, Is.EqualTo("~/foo/hello")); + + var defaultVirtualPath4 = routes.GetVirtualPath(requestContext, defaultRouteData4.Values); + Assert.That(defaultVirtualPath4, Is.Not.Null); + Assert.That(defaultVirtualPath4.VirtualPath, Is.EqualTo("~/foo/")); + } } } diff --git a/src/Orchard.Tests/Mvc/Routes/UrlPrefixTests.cs b/src/Orchard.Tests/Mvc/Routes/UrlPrefixTests.cs new file mode 100644 index 000000000..37255f345 --- /dev/null +++ b/src/Orchard.Tests/Mvc/Routes/UrlPrefixTests.cs @@ -0,0 +1,71 @@ +using NUnit.Framework; +using Orchard.Mvc.Routes; + +namespace Orchard.Tests.Mvc.Routes { + [TestFixture] + public class UrlPrefixTests { + [Test] + public void RemoveLeadingSegmentsOnlyMatchesFullSegment() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/bar")); + Assert.That(prefix.RemoveLeadingSegments("~/fooo/bar"), Is.EqualTo("~/fooo/bar")); + Assert.That(prefix.RemoveLeadingSegments("~/fo/bar"), Is.EqualTo("~/fo/bar")); + } + + [Test] + public void RemoveLeadingSegmentsMayContainSlash() { + var prefix = new UrlPrefix("foo/quux"); + Assert.That(prefix.RemoveLeadingSegments("~/foo/quux/bar"), Is.EqualTo("~/bar")); + Assert.That(prefix.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/foo/bar")); + Assert.That(prefix.RemoveLeadingSegments("~/quux/bar"), Is.EqualTo("~/quux/bar")); + } + + [Test] + public void RemoveLeadingSegmentsCanMatchEntireUrl() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.RemoveLeadingSegments("~/foo/"), Is.EqualTo("~/")); + Assert.That(prefix.RemoveLeadingSegments("~/foo"), Is.EqualTo("~/")); + } + + [Test] + public void RemoveLeadingSegmentsIsCaseInsensitive() { + var prefix = new UrlPrefix("Foo"); + Assert.That(prefix.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/bar")); + Assert.That(prefix.RemoveLeadingSegments("~/FOO/BAR"), Is.EqualTo("~/BAR")); + } + + [Test] + public void RemoveLeadingSegmentsIgnoreLeadingAndTrailingCharactersOnInput() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/bar")); + var prefix2 = new UrlPrefix("~/foo"); + Assert.That(prefix2.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/bar")); + var prefix3 = new UrlPrefix("foo/"); + Assert.That(prefix3.RemoveLeadingSegments("~/foo/bar"), Is.EqualTo("~/bar")); + } + + [Test] + public void PrependLeadingSegmentsInsertsBeforeNormalVirtualPath() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.PrependLeadingSegments("~/bar"), Is.EqualTo("~/foo/bar")); + } + + [Test] + public void PrependLeadingSegmentsPreservesNatureOfIncomingPath() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.PrependLeadingSegments("~/bar"), Is.EqualTo("~/foo/bar")); + Assert.That(prefix.PrependLeadingSegments("/bar"), Is.EqualTo("/foo/bar")); + Assert.That(prefix.PrependLeadingSegments("bar"), Is.EqualTo("foo/bar")); + } + + [Test] + public void PrependLeadingSegmentsHandlesShortUrlConditionsAppropriately() { + var prefix = new UrlPrefix("foo"); + Assert.That(prefix.PrependLeadingSegments("~/"), Is.EqualTo("~/foo/")); + Assert.That(prefix.PrependLeadingSegments("/"), Is.EqualTo("/foo/")); + Assert.That(prefix.PrependLeadingSegments("~"), Is.EqualTo("~/foo/")); + Assert.That(prefix.PrependLeadingSegments(""), Is.EqualTo("foo/")); + } + + } +} diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 43e1a0240..00e849f60 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -169,6 +169,7 @@ + diff --git a/src/Orchard.Tests/Stubs/StubHttpContext.cs b/src/Orchard.Tests/Stubs/StubHttpContext.cs index d8495e5a1..df66a262a 100644 --- a/src/Orchard.Tests/Stubs/StubHttpContext.cs +++ b/src/Orchard.Tests/Stubs/StubHttpContext.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; @@ -25,6 +26,10 @@ namespace Orchard.Tests.Stubs { public override HttpRequestBase Request { get { return new StubHttpRequest(this); } } + public override HttpResponseBase Response { + get { return new StubHttpResponse(this); } + } + public override IDictionary Items { get { return _items; } @@ -56,7 +61,18 @@ namespace Orchard.Tests.Stubs { ?? new NameValueCollection { { "HTTP_HOST", _httpContext._hostHeader } }; } } - } + + class StubHttpResponse : HttpResponseBase { + private readonly StubHttpContext _httpContext; + + public StubHttpResponse(StubHttpContext httpContext) { + _httpContext = httpContext; + } + public override string ApplyAppPathModifier(string virtualPath) { + return "~/" + virtualPath.TrimStart('/'); + } + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs b/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs index bd38f87e4..98bcddd67 100644 --- a/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs +++ b/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs @@ -86,7 +86,7 @@ namespace Orchard.Core.Settings.Topology { } _eventBus.Notify( - typeof(IShellDescriptorManager).FullName + ".UpdateShellDescriptor", + "ShellDescriptor_Changed", null); } } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt b/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt index d82dcda7f..67d16c464 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt @@ -1,2 +1,7 @@ name: Blogs -antiforgery: enabled \ No newline at end of file +antiforgery: enabled +features: + Orchard.Blogs: + Description: A simple web log + Dependencies: Common, XmlRpc + Category: Blog \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.Modules/AdminMenu.cs index d12009d21..fc8439236 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/AdminMenu.cs @@ -5,12 +5,12 @@ namespace Orchard.Modules { public string MenuName { get { return "admin"; } } public void GetNavigation(NavigationBuilder builder) { - builder.Add("Modules", "10", + builder.Add("Features", "10", menu => menu - .Add("Manage Modules", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.Modules" }) - .Permission(Permissions.ManageModules)) - .Add("Manage Features", "2.0", item => item.Action("Features", "Admin", new { area = "Orchard.Modules" }) - .Permission(Permissions.ManageFeatures))); + .Add("Manage Features", "1.0", item => item.Action("Features", "Admin", new { area = "Orchard.Modules" }) + .Permission(Permissions.ManageFeatures)) + .Add("Manage Modules", "2.0", item => item.Action("Index", "Admin", new { area = "Orchard.Modules" }) + .Permission(Permissions.ManageModules))); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/disabled.gif b/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/disabled.gif new file mode 100644 index 000000000..42c8bde22 Binary files /dev/null and b/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/disabled.gif differ diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/enabled.gif b/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/enabled.gif new file mode 100644 index 000000000..f55c73a2f Binary files /dev/null and b/src/Orchard.Web/Modules/Orchard.Modules/Content/Admin/images/enabled.gif differ diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs index 8b7704dad..47d8fc3a0 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs @@ -1,8 +1,13 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Web.Mvc; using Orchard.Localization; using Orchard.Modules.ViewModels; +using Orchard.Mvc.AntiForgery; using Orchard.Mvc.Results; +using Orchard.UI.Notify; namespace Orchard.Modules.Controllers { public class AdminController : Controller { @@ -39,12 +44,85 @@ namespace Orchard.Modules.Controllers { }); } - public ActionResult Features() { + public ActionResult Features(FeaturesOptions options) { if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) return new HttpUnauthorizedResult(); var features = _moduleService.GetAvailableFeatures(); - return View(new FeatureListViewModel {Features = features}); + return View(new FeaturesViewModel {Features = features, Options = options}); + } + + [HttpPost, ActionName("Features")] + [FormValueRequired("submit.BulkEdit")] + public ActionResult FeaturesPOST(FeaturesOptions options, IList selection) { + if (selection != null && selection.Count > 0) + { + switch (options.BulkAction) + { + case FeaturesBulkAction.None: + break; + case FeaturesBulkAction.Enable: + if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to enable features"))) + return new HttpUnauthorizedResult(); + _moduleService.EnableFeatures(selection); + //todo: (heskew) need better messages + //todo: (heskew) hmmm...need a helper to comma-separate all but last, which would get the " and " treatment...all localized, of course + Services.Notifier.Information(T("{0} were enabled", string.Join(", ", selection.ToArray()))); + break; + case FeaturesBulkAction.Disable: + if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to disable features"))) + return new HttpUnauthorizedResult(); + _moduleService.DisableFeatures(selection); + //todo: (heskew) need better messages + Services.Notifier.Information(T("{0} were disabled", string.Join(", ", selection.ToArray()))); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + return RedirectToAction("Features"); + } + + [ValidateAntiForgeryTokenOrchard] + public ActionResult Enable(string featureName) { + if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) + return new HttpUnauthorizedResult(); + + if (string.IsNullOrEmpty(featureName)) + return new NotFoundResult(); + + _moduleService.EnableFeatures(new [] {featureName}); + Services.Notifier.Information(T("{0} was enabled", featureName)); + + return RedirectToAction("Features"); + } + + [ValidateAntiForgeryTokenOrchard] + public ActionResult Disable(string featureName) { + if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) + return new HttpUnauthorizedResult(); + + if (string.IsNullOrEmpty(featureName)) + return new NotFoundResult(); + + _moduleService.DisableFeatures(new[] { featureName }); + Services.Notifier.Information(T("{0} was disabled", featureName)); + + return RedirectToAction("Features"); + } + + private class FormValueRequiredAttribute : ActionMethodSelectorAttribute { + private readonly string _submitButtonName; + + public FormValueRequiredAttribute(string submitButtonName) { + _submitButtonName = submitButtonName; + } + + public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { + var value = controllerContext.HttpContext.Request.Form[_submitButtonName]; + return !string.IsNullOrEmpty(value); + } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Models/ModuleFeature.cs b/src/Orchard.Web/Modules/Orchard.Modules/Models/ModuleFeature.cs new file mode 100644 index 000000000..5d947048e --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Modules/Models/ModuleFeature.cs @@ -0,0 +1,8 @@ +using Orchard.Environment.Extensions.Models; + +namespace Orchard.Modules.Models { + public class ModuleFeature : IModuleFeature { + public FeatureDescriptor Descriptor { get; set; } + public bool IsEnabled { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj b/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj index 4b092dbcc..a1206df0f 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj +++ b/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj @@ -63,7 +63,9 @@ - + + + @@ -85,6 +87,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs b/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs index 51b69717c..aaccb5679 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs @@ -7,9 +7,11 @@ using Orchard.Mvc.Routes; namespace Orchard.Modules { public class Routes : IRouteProvider { private readonly IModuleNameConstraint _moduleNameConstraint; + private readonly IFeatureNameConstraint _featureNameConstraint; - public Routes(IModuleNameConstraint moduleNameConstraint) { + public Routes(IModuleNameConstraint moduleNameConstraint, IFeatureNameConstraint featureNameConstraint) { _moduleNameConstraint = moduleNameConstraint; + _featureNameConstraint = featureNameConstraint; } public IEnumerable GetRoutes() { @@ -29,6 +31,38 @@ namespace Orchard.Modules { {"area", "Orchard.Modules"} }, new MvcRouteHandler()) + }, + new RouteDescriptor { + Route = new Route( + "Admin/Modules/Enable/{featureName}", + new RouteValueDictionary { + {"area", "Orchard.Modules"}, + {"controller", "Admin"}, + {"action", "Enable"} + }, + new RouteValueDictionary { + {"featureName", _featureNameConstraint} + }, + new RouteValueDictionary { + {"area", "Orchard.Modules"} + }, + new MvcRouteHandler()) + }, + new RouteDescriptor { + Route = new Route( + "Admin/Modules/Disable/{featureName}", + new RouteValueDictionary { + {"area", "Orchard.Modules"}, + {"controller", "Admin"}, + {"action", "Disable"} + }, + new RouteValueDictionary { + {"featureName", _featureNameConstraint} + }, + new RouteValueDictionary { + {"area", "Orchard.Modules"} + }, + new MvcRouteHandler()) } }; } diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs b/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs new file mode 100644 index 000000000..bee40d3c1 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; +using System.Web; +using System.Web.Routing; + +namespace Orchard.Modules.Routing { + public interface IFeatureNameConstraint : IRouteConstraint, ISingletonDependency { + } + + public class FeatureNameConstraint : IFeatureNameConstraint { + private readonly IModuleService _moduleService; + + public FeatureNameConstraint(IModuleService moduleService) { + _moduleService = moduleService; + } + + public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { + if (routeDirection == RouteDirection.UrlGeneration) + return true; + + object value; + if (values.TryGetValue(parameterName, out value)) + return _moduleService.GetModuleByFeatureName(Convert.ToString(value)) != null; + + return false; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs index 528827c44..e75b70d45 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs @@ -4,19 +4,23 @@ using System.Linq; using System.Web; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; +using Orchard.Environment.Topology; +using Orchard.Environment.Topology.Models; using Orchard.Modules.Models; namespace Orchard.Modules.Services { public class ModuleService : IModuleService { private const string ModuleExtensionType = "module"; private readonly IExtensionManager _extensionManager; + private readonly IShellDescriptorManager _shellDescriptorManager; - public ModuleService(IExtensionManager extensionManager) { + public ModuleService(IExtensionManager extensionManager, IShellDescriptorManager shellDescriptorManager) { _extensionManager = extensionManager; + _shellDescriptorManager = shellDescriptorManager; } public IModule GetModuleByName(string moduleName) { - return _extensionManager.AvailableExtensions().Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && string.Equals(e.ExtensionType, "Module", StringComparison.OrdinalIgnoreCase)).Select( + return _extensionManager.AvailableExtensions().Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && string.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault(); } @@ -35,24 +39,41 @@ namespace Orchard.Modules.Services { _extensionManager.UninstallExtension(ModuleExtensionType, moduleName); } - public IEnumerable GetAvailableFeatures() { + public IModule GetModuleByFeatureName(string featureName) { return GetInstalledModules() - .Where(m => m.Features != null) - .SelectMany(m => _extensionManager.LoadFeatures(m.Features)); + .Where( + m => + m.Features.FirstOrDefault(f => string.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) != + null).FirstOrDefault(); + } + + public IEnumerable GetAvailableFeatures() { + var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures; + return GetInstalledModules() + .SelectMany(m => _extensionManager.LoadFeatures(m.Features)) + .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null)); } public IEnumerable GetAvailableFeaturesByModule(string moduleName) { - var module = GetModuleByName(moduleName); - if (module == null || module.Features == null) - return null; - - return _extensionManager.LoadFeatures(module.Features); + throw new NotImplementedException(); } public void EnableFeatures(IEnumerable featureNames) { + var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); + + var enabledFeatures = shellDescriptor.EnabledFeatures + .Union(featureNames.Select(s => new ShellFeature {Name = s})); + + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); } public void DisableFeatures(IEnumerable featureNames) { + var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); + + var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); + enabledFeatures.RemoveAll(f => featureNames.Contains(f.Name)); + + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); } private static IModule AssembleModuleFromDescriptor(ExtensionDescriptor extensionDescriptor) { @@ -67,5 +88,12 @@ namespace Orchard.Modules.Services { Features = extensionDescriptor.Features }; } + + private static IModuleFeature AssembleModuleFromDescriptor(Feature feature, bool isEnabled) { + return new ModuleFeature { + Descriptor = feature.Descriptor, + IsEnabled = isEnabled + }; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeatureListViewModel.cs b/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeatureListViewModel.cs deleted file mode 100644 index 31815667e..000000000 --- a/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeatureListViewModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using Orchard.Environment.Extensions.Models; -using Orchard.Mvc.ViewModels; - -namespace Orchard.Modules.ViewModels { - public class FeatureListViewModel : BaseViewModel { - public IEnumerable Features { get; set; } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs b/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs new file mode 100644 index 000000000..210ce308c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Orchard.Mvc.ViewModels; + +namespace Orchard.Modules.ViewModels { + public class FeaturesViewModel : BaseViewModel { + public IEnumerable Features { get; set; } + public FeaturesOptions Options { get; set; } + } + + public class FeaturesOptions { + public FeaturesBulkAction BulkAction { get; set; } + } + + public enum FeaturesBulkAction { + None, + Enable, + Disable + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx index 4482efca5..b604c24c6 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx @@ -1,33 +1,68 @@ -<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Modules.ViewModels"%>

<%=Html.TitleForPage(T("Manage Features").ToString()) %>

-

<%=T("Available Features") %>

-<% if (Model.Features.Count() > 0) { %> -
    <% - foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Name).GroupBy(f => f.Descriptor.Category)) { %> -
  • -

    <%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("General")) %>

    -
      <% - foreach (var feature in featureGroup.Select(f => f.Descriptor)) {%> -
    • -

      <%=Html.Encode(feature.Name) %>

      -

      <%=T("From: {0}", Html.Encode(feature.Extension.DisplayName)) %>

      <% - if (!string.IsNullOrEmpty(feature.Description)) { %> -

      <%=Html.Encode(feature.Description) %>

      <% - } - if (feature.Dependencies.Count() > 0) {%> -
      <%=_Encoded("Depends on:")%>
      + +<% if (Model.Features.Count() > 0) { + +using (Html.BeginFormAntiForgeryPost()) { %> + <%=Html.ValidationSummary()%> +
      + + + " /> +
      +
      +
        <% + foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Name).GroupBy(f => f.Descriptor.Category)) { %> + > +

        <%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("General")) %>

          <% - foreach (var dependency in feature.Dependencies) { %> -
        • <%=Html.Encode(dependency) %>
        • <% + foreach (var feature in featureGroup) {%> + > +
          +
          + +

          <%=Html.Encode(feature.Descriptor.Name) %>

          +
            +
          • <% + //enabled or not + if (feature.IsEnabled) { %> + " alt="<%=_Encoded("Enabled") %>" title="<%=_Encoded("This feature is currently enabled") %>" /><%=_Encoded("Enabled") %><% + } + else { %> + " alt="<%=_Encoded("Disabled") %>" title="<%=_Encoded("This feature is currently disabled") %>" /><%=_Encoded("Disabled")%><% + } %> +
          • <% + //dependencies + if (feature.Descriptor.Dependencies.Count() > 0) { %> +
          •  | <%=_Encoded("Depends on: ") %><% + foreach (var dependency in feature.Descriptor.Dependencies) { + %><% if (dependency != feature.Descriptor.Dependencies.First()) { %><%=_Encoded(", ") %><% } + %><%=Html.Encode(dependency) %><% + } %> +
          • <% + } %> +
          +
          + +
          + <% } %> -
        <% - }%> +
    • <% } %> -
    -
  • <% - } %> -
<% - } %> \ No newline at end of file + <% + } %> + <% +} %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx index ad5a45bb2..ca0588562 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx @@ -2,8 +2,10 @@ <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Modules.ViewModels"%>

<%=Html.TitleForPage(T("Manage Modules").ToString()) %>

+
<%=Html.ActionLink(T("Install a module").ToString(), "Features", new { }, new { @class = "button primaryAction" })%>

<%=T("Installed Modules") %>

<% if (Model.Modules.Count() > 0) { %> +
    <% foreach (var module in Model.Modules.OrderBy(m => m.DisplayName)) { %>
  • @@ -21,5 +23,6 @@ } %>
  • <% } %> -
<% + +
<% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs index f4fd6ecbd..d838927e1 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs @@ -12,20 +12,12 @@ namespace Orchard.MultiTenancy.Services { _orchardHost = orchardHost; } - #region Implementation of ITenantService - public IEnumerable GetTenants() { return _shellSettingsManager.LoadSettings(); } public void CreateTenant(ShellSettings settings) { _shellSettingsManager.SaveSettings(settings); - - - // MultiTenancy: This will not be needed when host listens to event bus - _orchardHost.Reinitialize_Obsolete(); } - - #endregion } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx index 048ce3936..eb6aca4dc 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx @@ -11,8 +11,6 @@

- -
" /> diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css index cf3ecef3f..768ef4a9f 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css +++ b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css @@ -607,6 +607,9 @@ button.ibutton { .contentItems li.first { background:#fff url(images/backgroundGradient.gif) repeat-x top left; } +.contentItems li li.last { + border-bottom:0; +} #main .contentItems li h3 { border-bottom:0; margin-top:0; @@ -753,6 +756,7 @@ table.items, textarea, input.text, input.text-box, margin-top:1em; } .summary { + overflow:auto; padding:1.2em .4em; } .actions { diff --git a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs index d1de2a657..0267bc955 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Yaml.Serialization; +using Orchard.Events; using Orchard.Localization; namespace Orchard.Environment.Configuration { @@ -13,10 +14,12 @@ namespace Orchard.Environment.Configuration { public class ShellSettingsManager : IShellSettingsManager { private readonly IAppDataFolder _appDataFolder; + private readonly IEventBus _eventBus; Localizer T { get; set; } - public ShellSettingsManager(IAppDataFolder appDataFolder) { + public ShellSettingsManager(IAppDataFolder appDataFolder, IEventBus eventBus) { _appDataFolder = appDataFolder; + _eventBus = eventBus; T = NullLocalizer.Instance; } @@ -32,6 +35,8 @@ namespace Orchard.Environment.Configuration { var filePath = Path.Combine(Path.Combine("Sites", settings.Name), "Settings.txt"); _appDataFolder.CreateFile(filePath, ComposeSettings(settings)); + + _eventBus.Notify("ShellSettings_Saved", null); } IEnumerable LoadSettings() { diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 3f6e182d3..35a1f394b 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -1,7 +1,7 @@ +using System; using System.Linq; using System.Web.Mvc; using Autofac; -using Autofac.Integration.Web; using System.Collections.Generic; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -131,5 +131,13 @@ namespace Orchard.Environment { } } + public void Process(string messageName, IDictionary eventData) { + if (messageName == "ShellSettings_Saved") { + _current = null; + } + else if (messageName == "ShellDescriptor_Changed") { + _current = null; + } + } } } diff --git a/src/Orchard/Environment/DefaultOrchardHostEventSink.cs b/src/Orchard/Environment/DefaultOrchardHostEventSink.cs new file mode 100644 index 000000000..9d0542e21 --- /dev/null +++ b/src/Orchard/Environment/DefaultOrchardHostEventSink.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Orchard.Events; + +namespace Orchard.Environment { + /// + /// This handler forwards calls to the IOrchardHost when it is an instance of DefaultOrchardHost. + /// The reason for this is to avoid adding IEventBusHandler, because DefaultOrchardHost is a component + /// that should not be detected and registererd automatically as an IDependency. + /// + public class DefaultOrchardHostEventSink : IEventBusHandler { + private readonly DefaultOrchardHost _host; + + public DefaultOrchardHostEventSink(IOrchardHost host) { + _host = host as DefaultOrchardHost; + } + + public void Process(string messageName, IDictionary eventData) { + if (_host != null) { + _host.Process(messageName, eventData); + } + } + } +} diff --git a/src/Orchard/Environment/IOrchardHost.cs b/src/Orchard/Environment/IOrchardHost.cs index 314560677..754ca84a0 100644 --- a/src/Orchard/Environment/IOrchardHost.cs +++ b/src/Orchard/Environment/IOrchardHost.cs @@ -27,6 +27,5 @@ namespace Orchard.Environment { /// Services may be resolved from within this instance to configure and initialize it's storage. /// IStandaloneEnvironment CreateStandaloneEnvironment(ShellSettings shellSettings); - } - + } } diff --git a/src/Orchard/Events/DefaultOrchardEventBus.cs b/src/Orchard/Events/DefaultOrchardEventBus.cs index d1ae5652d..820b1fa55 100644 --- a/src/Orchard/Events/DefaultOrchardEventBus.cs +++ b/src/Orchard/Events/DefaultOrchardEventBus.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Orchard.Logging; namespace Orchard.Events { public class DefaultOrchardEventBus : IEventBus { - private readonly IEnumerable _handlers; + private readonly Func> _handlers; - public DefaultOrchardEventBus(IEnumerable handlers) { + public DefaultOrchardEventBus(Func> handlers) { _handlers = handlers; Logger = NullLogger.Instance; } @@ -15,7 +16,7 @@ namespace Orchard.Events { #region Implementation of IEventBus public void Notify(string messageName, IDictionary eventData) { - _handlers.Invoke(handler => handler.Process(messageName, eventData), Logger); + _handlers().Invoke(handler => handler.Process(messageName, eventData), Logger); } #endregion diff --git a/src/Orchard/Modules/IModuleFeature.cs b/src/Orchard/Modules/IModuleFeature.cs new file mode 100644 index 000000000..cc59efa9b --- /dev/null +++ b/src/Orchard/Modules/IModuleFeature.cs @@ -0,0 +1,8 @@ +using Orchard.Environment.Extensions.Models; + +namespace Orchard.Modules { + public interface IModuleFeature { + FeatureDescriptor Descriptor { get; set; } + bool IsEnabled { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard/Modules/IModuleService.cs b/src/Orchard/Modules/IModuleService.cs index a7c67b3d2..d2792315e 100644 --- a/src/Orchard/Modules/IModuleService.cs +++ b/src/Orchard/Modules/IModuleService.cs @@ -8,7 +8,8 @@ namespace Orchard.Modules { IEnumerable GetInstalledModules(); void InstallModule(HttpPostedFileBase file); void UninstallModule(string moduleName); - IEnumerable GetAvailableFeatures(); + IModule GetModuleByFeatureName(string featureName); + IEnumerable GetAvailableFeatures(); void EnableFeatures(IEnumerable featureNames); void DisableFeatures(IEnumerable featureNames); } diff --git a/src/Orchard/Mvc/Routes/ShellRoute.cs b/src/Orchard/Mvc/Routes/ShellRoute.cs index 6ead5e404..3a0585bb4 100644 --- a/src/Orchard/Mvc/Routes/ShellRoute.cs +++ b/src/Orchard/Mvc/Routes/ShellRoute.cs @@ -16,12 +16,15 @@ namespace Orchard.Mvc.Routes { private readonly ShellSettings _shellSettings; private readonly IContainer _container; private readonly IRunningShellTable _runningShellTable; + private UrlPrefix _urlPrefix; public ShellRoute(RouteBase route, ShellSettings shellSettings, ILifetimeScope shellLifetimeScope, IRunningShellTable runningShellTable) { _route = route; _shellSettings = shellSettings; _runningShellTable = runningShellTable; _container = new LifetimeScopeContainer(shellLifetimeScope); + if (!string.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) + _urlPrefix = new UrlPrefix(_shellSettings.RequestUrlPrefix); var routeWithArea = route as IRouteWithArea; if (routeWithArea != null) { @@ -45,8 +48,11 @@ namespace Orchard.Mvc.Routes { if (settings == null || settings.Name != _shellSettings.Name) return null; - // route didn't match anyway - var routeData = _route.GetRouteData(httpContext); + var effectiveHttpContext = httpContext; + if (_urlPrefix != null) + effectiveHttpContext = new UrlPrefixAdjustedHttpContext(httpContext, _urlPrefix); + + var routeData = _route.GetRouteData(effectiveHttpContext); if (routeData == null) return null; @@ -59,12 +65,24 @@ namespace Orchard.Mvc.Routes { public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { - // todo - ignore conditionally + // locate appropriate shell settings for request + var settings = _runningShellTable.Match(requestContext.HttpContext); - var virtualPath = _route.GetVirtualPath(requestContext, values); + // only proceed if there was a match, and it was for this client + if (settings == null || settings.Name != _shellSettings.Name) + return null; + + var effectiveRequestContext = requestContext; + if (_urlPrefix != null) + effectiveRequestContext = new RequestContext(new UrlPrefixAdjustedHttpContext(requestContext.HttpContext, _urlPrefix), requestContext.RouteData); + + var virtualPath = _route.GetVirtualPath(effectiveRequestContext, values); if (virtualPath == null) return null; + if (_urlPrefix != null) + virtualPath.VirtualPath = _urlPrefix.PrependLeadingSegments(virtualPath.VirtualPath); + return virtualPath; } diff --git a/src/Orchard/Mvc/Routes/UrlPrefix.cs b/src/Orchard/Mvc/Routes/UrlPrefix.cs new file mode 100644 index 000000000..0faa4bd12 --- /dev/null +++ b/src/Orchard/Mvc/Routes/UrlPrefix.cs @@ -0,0 +1,56 @@ +using System; + +namespace Orchard.Mvc.Routes { + /// + /// Small worker class to perform path prefix adjustments + /// + public class UrlPrefix { + private readonly string _prefix; + + public UrlPrefix(string prefix) { + _prefix = prefix.TrimStart('~').Trim('/'); + } + + public string RemoveLeadingSegments(string path) { + var beginIndex = 0; + if (path.Length > beginIndex && path[beginIndex] == '~') + ++beginIndex; + if (path.Length > beginIndex && path[beginIndex] == '/') + ++beginIndex; + + var endIndex = beginIndex + _prefix.Length; + if (path.Length == endIndex) { + // no-op + } + else if (path.Length > endIndex && path[endIndex] == '/') { + // don't include slash after segment in result + ++endIndex; + } + else { + // too short to compare - return unmodified + return path; + } + + if (string.Compare(path, beginIndex, _prefix, 0, _prefix.Length, StringComparison.OrdinalIgnoreCase) == 0) { + return path.Substring(0, beginIndex) + path.Substring(endIndex); + } + + return path; + } + + public string PrependLeadingSegments(string path) { + if (path == "~") { + // special case for peculiar situation + return "~/" + _prefix + "/"; + } + + var index = 0; + if (path.Length > index && path[index] == '~') + ++index; + if (path.Length > index && path[index] == '/') + ++index; + + return path.Substring(0, index) + _prefix + '/' + path.Substring(index); + } + } +} diff --git a/src/Orchard/Mvc/Routes/UrlPrefixAdjustedHttpContext.cs b/src/Orchard/Mvc/Routes/UrlPrefixAdjustedHttpContext.cs new file mode 100644 index 000000000..41ba52951 --- /dev/null +++ b/src/Orchard/Mvc/Routes/UrlPrefixAdjustedHttpContext.cs @@ -0,0 +1,36 @@ +using System; +using System.Web; +using Orchard.Mvc.Wrappers; + +namespace Orchard.Mvc.Routes { + public class UrlPrefixAdjustedHttpContext : HttpContextBaseWrapper { + private readonly UrlPrefix _prefix; + + public UrlPrefixAdjustedHttpContext(HttpContextBase httpContextBase, UrlPrefix prefix) + : base(httpContextBase) { + _prefix = prefix; + } + + public override HttpRequestBase Request { + get { + return new AdjustedRequest(_httpContextBase.Request, _prefix); + } + } + + class AdjustedRequest : HttpRequestBaseWrapper { + private readonly UrlPrefix _prefix; + + public AdjustedRequest(HttpRequestBase httpRequestBase, UrlPrefix prefix) + : base(httpRequestBase) { + _prefix = prefix; + } + + public override string AppRelativeCurrentExecutionFilePath { + get { + return _prefix.RemoveLeadingSegments(_httpRequestBase.AppRelativeCurrentExecutionFilePath); + } + } + } + + } +} diff --git a/src/Orchard/Mvc/Wrappers/HttpContextBaseWrapper.cs b/src/Orchard/Mvc/Wrappers/HttpContextBaseWrapper.cs new file mode 100644 index 000000000..6261f2217 --- /dev/null +++ b/src/Orchard/Mvc/Wrappers/HttpContextBaseWrapper.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections; +using System.Globalization; +using System.Security.Principal; +using System.Web; +using System.Web.Caching; +using System.Web.Profile; + +namespace Orchard.Mvc.Wrappers { + public abstract class HttpContextBaseWrapper : HttpContextBase { + protected readonly HttpContextBase _httpContextBase; + + protected HttpContextBaseWrapper(HttpContextBase httpContextBase) { + _httpContextBase = httpContextBase; + } + + public override void AddError(Exception errorInfo) { + _httpContextBase.AddError(errorInfo); + } + + public override void ClearError() { + _httpContextBase.ClearError(); + } + + public override object GetGlobalResourceObject(string classKey, string resourceKey) { + return _httpContextBase.GetGlobalResourceObject(classKey, resourceKey); + } + + public override object GetGlobalResourceObject(string classKey, string resourceKey, CultureInfo culture) { + return _httpContextBase.GetGlobalResourceObject(classKey, resourceKey, culture); + } + + public override object GetLocalResourceObject(string virtualPath, string resourceKey) { + return _httpContextBase.GetLocalResourceObject(virtualPath, resourceKey); + } + + public override object GetLocalResourceObject(string virtualPath, string resourceKey, CultureInfo culture) { + return _httpContextBase.GetLocalResourceObject(virtualPath, resourceKey, culture); + } + + public override object GetSection(string sectionName) { + return _httpContextBase.GetSection(sectionName); + } + + public override object GetService(Type serviceType) { + return ((IServiceProvider)_httpContextBase).GetService(serviceType); + } + + public override void RewritePath(string path) { + _httpContextBase.RewritePath(path); + } + + public override void RewritePath(string path, bool rebaseClientPath) { + _httpContextBase.RewritePath(path, rebaseClientPath); + } + + public override void RewritePath(string filePath, string pathInfo, string queryString) { + _httpContextBase.RewritePath(filePath, pathInfo, queryString); + } + + public override void RewritePath(string filePath, string pathInfo, string queryString, bool setClientFilePath) { + _httpContextBase.RewritePath(filePath, pathInfo, queryString, setClientFilePath); + } + + public override Exception[] AllErrors { + get { + return _httpContextBase.AllErrors; + } + } + + public override HttpApplicationStateBase Application { + get { + return _httpContextBase.Application; + } + } + + public override HttpApplication ApplicationInstance { + get { + return _httpContextBase.ApplicationInstance; + } + set { + _httpContextBase.ApplicationInstance = value; + } + } + + public override Cache Cache { + get { + return _httpContextBase.Cache; + } + } + + public override IHttpHandler CurrentHandler { + get { + return _httpContextBase.CurrentHandler; + } + } + + public override RequestNotification CurrentNotification { + get { + return _httpContextBase.CurrentNotification; + } + } + + public override Exception Error { + get { + return _httpContextBase.Error; + } + } + + public override IHttpHandler Handler { + get { + return _httpContextBase.Handler; + } + set { + _httpContextBase.Handler = value; + } + } + + public override bool IsCustomErrorEnabled { + get { + return _httpContextBase.IsCustomErrorEnabled; + } + } + + public override bool IsDebuggingEnabled { + get { + return _httpContextBase.IsDebuggingEnabled; + } + } + + public override bool IsPostNotification { + get { + return _httpContextBase.IsDebuggingEnabled; + } + } + + public override IDictionary Items { + get { + return _httpContextBase.Items; + } + } + + public override IHttpHandler PreviousHandler { + get { + return _httpContextBase.PreviousHandler; + } + } + + public override ProfileBase Profile { + get { + return _httpContextBase.Profile; + } + } + + public override HttpRequestBase Request { + get { + return _httpContextBase.Request; + } + } + + public override HttpResponseBase Response { + get { + return _httpContextBase.Response; + } + } + + public override HttpServerUtilityBase Server { + get { + return _httpContextBase.Server; + } + } + + public override HttpSessionStateBase Session { + get { + return _httpContextBase.Session; + } + } + + public override bool SkipAuthorization { + get { + return _httpContextBase.SkipAuthorization; + } + set { + _httpContextBase.SkipAuthorization = value; + } + } + + public override DateTime Timestamp { + get { + return _httpContextBase.Timestamp; + } + } + + public override TraceContext Trace { + get { + return _httpContextBase.Trace; + } + } + + public override IPrincipal User { + get { + return _httpContextBase.User; + } + set { + _httpContextBase.User = value; + } + } + + + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/Wrappers/HttpRequestBaseWrapper.cs b/src/Orchard/Mvc/Wrappers/HttpRequestBaseWrapper.cs new file mode 100644 index 000000000..82bea62ba --- /dev/null +++ b/src/Orchard/Mvc/Wrappers/HttpRequestBaseWrapper.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Specialized; +using System.IO; +using System.Security.Principal; +using System.Text; +using System.Web; + +namespace Orchard.Mvc.Wrappers { + public abstract class HttpRequestBaseWrapper : HttpRequestBase { + protected readonly HttpRequestBase _httpRequestBase; + + protected HttpRequestBaseWrapper(HttpRequestBase httpRequestBase) { + _httpRequestBase = httpRequestBase; + } + + public override byte[] BinaryRead(int count) { + return _httpRequestBase.BinaryRead(count); + } + + public override int[] MapImageCoordinates(string imageFieldName) { + return _httpRequestBase.MapImageCoordinates(imageFieldName); + } + + public override string MapPath(string virtualPath) { + return _httpRequestBase.MapPath(virtualPath); + } + + public override string MapPath(string virtualPath, string baseVirtualDir, bool allowCrossAppMapping) { + return _httpRequestBase.MapPath(virtualPath, baseVirtualDir, allowCrossAppMapping); + } + + public override void SaveAs(string filename, bool includeHeaders) { + _httpRequestBase.SaveAs(filename, includeHeaders); + } + + public override void ValidateInput() { + _httpRequestBase.ValidateInput(); + } + + public override string[] AcceptTypes { + get { + return _httpRequestBase.AcceptTypes; + } + } + + public override string AnonymousID { + get { + return _httpRequestBase.AnonymousID; + } + } + + public override string ApplicationPath { + get { + return _httpRequestBase.ApplicationPath; + } + } + + public override string AppRelativeCurrentExecutionFilePath { + get { + return _httpRequestBase.AppRelativeCurrentExecutionFilePath; + } + } + + public override HttpBrowserCapabilitiesBase Browser { + get { + return _httpRequestBase.Browser; + } + } + + public override HttpClientCertificate ClientCertificate { + get { + return _httpRequestBase.ClientCertificate; + } + } + + public override Encoding ContentEncoding { + get { + return _httpRequestBase.ContentEncoding; + } + set { + _httpRequestBase.ContentEncoding = value; + } + } + + public override int ContentLength { + get { + return _httpRequestBase.ContentLength; + } + } + + public override string ContentType { + get { + return _httpRequestBase.ContentType; + } + set { + _httpRequestBase.ContentType = value; + } + } + + public override HttpCookieCollection Cookies { + get { + return _httpRequestBase.Cookies; + } + } + + public override string CurrentExecutionFilePath { + get { + return _httpRequestBase.CurrentExecutionFilePath; + } + } + + public override string FilePath { + get { + return _httpRequestBase.FilePath; + } + } + + public override HttpFileCollectionBase Files { + get { + return _httpRequestBase.Files; + } + } + + public override Stream Filter { + get { + return _httpRequestBase.Filter; + } + set { + _httpRequestBase.Filter = value; + } + } + + public override NameValueCollection Form { + get { + return _httpRequestBase.Form; + } + } + + public override NameValueCollection Headers { + get { + return _httpRequestBase.Headers; + } + } + + public override string HttpMethod { + get { + return _httpRequestBase.HttpMethod; + } + } + + public override Stream InputStream { + get { + return _httpRequestBase.InputStream; + } + } + + public override bool IsAuthenticated { + get { + return _httpRequestBase.IsAuthenticated; + } + } + + public override bool IsLocal { + get { + return _httpRequestBase.IsLocal; + } + } + + public override bool IsSecureConnection { + get { + return _httpRequestBase.IsSecureConnection; + } + } + + public override string this[string key] { + get { + return _httpRequestBase[key]; + } + } + + public override WindowsIdentity LogonUserIdentity { + get { + return _httpRequestBase.LogonUserIdentity; + } + } + + public override NameValueCollection Params { + get { + return _httpRequestBase.Params; + } + } + + public override string Path { + get { + return _httpRequestBase.Path; + } + } + + public override string PathInfo { + get { + return _httpRequestBase.PathInfo; + } + } + + public override string PhysicalApplicationPath { + get { + return _httpRequestBase.PhysicalApplicationPath; + } + } + + public override string PhysicalPath { + get { + return _httpRequestBase.PhysicalPath; + } + } + + public override NameValueCollection QueryString { + get { + return _httpRequestBase.QueryString; + } + } + + public override string RawUrl { + get { + return _httpRequestBase.RawUrl; + } + } + + public override string RequestType { + get { + return _httpRequestBase.RequestType; + } + set { + _httpRequestBase.RequestType = value; + } + } + + public override NameValueCollection ServerVariables { + get { + return _httpRequestBase.ServerVariables; + } + } + + public override int TotalBytes { + get { + return _httpRequestBase.TotalBytes; + } + } + + public override Uri Url { + get { + return _httpRequestBase.Url; + } + } + + public override Uri UrlReferrer { + get { + return _httpRequestBase.UrlReferrer; + } + } + + public override string UserAgent { + get { + return _httpRequestBase.UserAgent; + } + } + + public override string UserHostAddress { + get { + return _httpRequestBase.UserHostAddress; + } + } + + public override string UserHostName { + get { + return _httpRequestBase.UserHostName; + } + } + + public override string[] UserLanguages { + get { + return _httpRequestBase.UserLanguages; + } + } + + } +} \ No newline at end of file diff --git a/src/Orchard/Mvc/Wrappers/HttpResponseBaseWrapper.cs b/src/Orchard/Mvc/Wrappers/HttpResponseBaseWrapper.cs new file mode 100644 index 000000000..5728503ce --- /dev/null +++ b/src/Orchard/Mvc/Wrappers/HttpResponseBaseWrapper.cs @@ -0,0 +1,358 @@ +using System; +using System.Collections; +using System.Collections.Specialized; +using System.IO; +using System.Text; +using System.Web; +using System.Web.Caching; + +namespace Orchard.Mvc.Wrappers { + public abstract class HttpResponseBaseWrapper : HttpResponseBase { + private readonly HttpResponseBase _httpResponseBase; + + protected HttpResponseBaseWrapper(HttpResponseBase httpResponse) { + _httpResponseBase = httpResponse; + } + + public override void AddCacheDependency(params CacheDependency[] dependencies) { + _httpResponseBase.AddCacheDependency(dependencies); + } + + public override void AddCacheItemDependencies(ArrayList cacheKeys) { + _httpResponseBase.AddCacheItemDependencies(cacheKeys); + } + + public override void AddCacheItemDependencies(string[] cacheKeys) { + _httpResponseBase.AddCacheItemDependencies(cacheKeys); + } + + public override void AddCacheItemDependency(string cacheKey) { + _httpResponseBase.AddCacheItemDependency(cacheKey); + } + + public override void AddFileDependencies(string[] filenames) { + _httpResponseBase.AddFileDependencies(filenames); + } + + public override void AddFileDependencies(ArrayList filenames) { + _httpResponseBase.AddFileDependencies(filenames); + } + + public override void AddFileDependency(string filename) { + _httpResponseBase.AddFileDependency(filename); + } + + public override void AddHeader(string name, string value) { + _httpResponseBase.AddHeader(name, value); + } + + public override void AppendCookie(HttpCookie cookie) { + _httpResponseBase.AppendCookie(cookie); + } + + public override void AppendHeader(string name, string value) { + _httpResponseBase.AppendHeader(name, value); + } + + public override void AppendToLog(string param) { + _httpResponseBase.AppendToLog(param); + } + + public override string ApplyAppPathModifier(string virtualPath) { + return _httpResponseBase.ApplyAppPathModifier(virtualPath); + } + + public override void BinaryWrite(byte[] buffer) { + _httpResponseBase.BinaryWrite(buffer); + } + + public override void Clear() { + _httpResponseBase.Clear(); + } + + public override void ClearContent() { + _httpResponseBase.ClearContent(); + } + + public override void ClearHeaders() { + _httpResponseBase.ClearHeaders(); + } + + public override void Close() { + _httpResponseBase.Close(); + } + + public override void DisableKernelCache() { + _httpResponseBase.DisableKernelCache(); + } + + public override void End() { + _httpResponseBase.End(); + } + + public override void Flush() { + _httpResponseBase.Flush(); + } + + public override void Pics(string value) { + _httpResponseBase.Pics(value); + } + + public override void Redirect(string url) { + _httpResponseBase.Redirect(url); + } + + public override void Redirect(string url, bool endResponse) { + _httpResponseBase.Redirect(url, endResponse); + } + + public override void RemoveOutputCacheItem(string path) { + _httpResponseBase.RemoveOutputCacheItem(path); + } + + public override void SetCookie(HttpCookie cookie) { + _httpResponseBase.SetCookie(cookie); + } + + public override void TransmitFile(string filename) { + _httpResponseBase.TransmitFile(filename); + } + + public override void TransmitFile(string filename, long offset, long length) { + _httpResponseBase.TransmitFile(filename, offset, length); + } + + public override void Write(char ch) { + _httpResponseBase.Write(ch); + } + + public override void Write(object obj) { + _httpResponseBase.Write(obj); + } + + public override void Write(string s) { + _httpResponseBase.Write(s); + } + + public override void Write(char[] buffer, int index, int count) { + _httpResponseBase.Write(buffer, index, count); + } + + public override void WriteFile(string filename) { + _httpResponseBase.WriteFile(filename); + } + + public override void WriteFile(string filename, bool readIntoMemory) { + _httpResponseBase.WriteFile(filename, readIntoMemory); + } + + public override void WriteFile(IntPtr fileHandle, long offset, long size) { + _httpResponseBase.WriteFile(fileHandle, offset, size); + } + + public override void WriteFile(string filename, long offset, long size) { + _httpResponseBase.WriteFile(filename, offset, size); + } + + public override void WriteSubstitution(HttpResponseSubstitutionCallback callback) { + _httpResponseBase.WriteSubstitution(callback); + } + + // Properties + public override bool Buffer { + get { + return _httpResponseBase.Buffer; + } + set { + _httpResponseBase.Buffer = value; + } + } + + public override bool BufferOutput { + get { + return _httpResponseBase.BufferOutput; + } + set { + _httpResponseBase.BufferOutput = value; + } + } + + public override HttpCachePolicyBase Cache { + get { + return _httpResponseBase.Cache; + } + } + + public override string CacheControl { + get { + return _httpResponseBase.CacheControl; + } + set { + _httpResponseBase.CacheControl = value; + } + } + + public override string Charset { + get { + return _httpResponseBase.Charset; + } + set { + _httpResponseBase.Charset = value; + } + } + + public override Encoding ContentEncoding { + get { + return _httpResponseBase.ContentEncoding; + } + set { + _httpResponseBase.ContentEncoding = value; + } + } + + public override string ContentType { + get { + return _httpResponseBase.ContentType; + } + set { + _httpResponseBase.ContentType = value; + } + } + + public override HttpCookieCollection Cookies { + get { + return _httpResponseBase.Cookies; + } + } + + public override int Expires { + get { + return _httpResponseBase.Expires; + } + set { + _httpResponseBase.Expires = value; + } + } + + public override DateTime ExpiresAbsolute { + get { + return _httpResponseBase.ExpiresAbsolute; + } + set { + _httpResponseBase.ExpiresAbsolute = value; + } + } + + public override Stream Filter { + get { + return _httpResponseBase.Filter; + } + set { + _httpResponseBase.Filter = value; + } + } + + public override Encoding HeaderEncoding { + get { + return _httpResponseBase.HeaderEncoding; + } + set { + _httpResponseBase.HeaderEncoding = value; + } + } + + public override NameValueCollection Headers { + get { + return _httpResponseBase.Headers; + } + } + + public override bool IsClientConnected { + get { + return _httpResponseBase.IsClientConnected; + } + } + + public override bool IsRequestBeingRedirected { + get { + return _httpResponseBase.IsRequestBeingRedirected; + } + } + + public override TextWriter Output { + get { + return _httpResponseBase.Output; + } + } + + public override Stream OutputStream { + get { + return _httpResponseBase.OutputStream; + } + } + + public override string RedirectLocation { + get { + return _httpResponseBase.RedirectLocation; + } + set { + _httpResponseBase.RedirectLocation = value; + } + } + + public override string Status { + get { + return _httpResponseBase.Status; + } + set { + _httpResponseBase.Status = value; + } + } + + public override int StatusCode { + get { + return _httpResponseBase.StatusCode; + } + set { + _httpResponseBase.StatusCode = value; + } + } + + public override string StatusDescription { + get { + return _httpResponseBase.StatusDescription; + } + set { + _httpResponseBase.StatusDescription = value; + } + } + + public override int SubStatusCode { + get { + return _httpResponseBase.SubStatusCode; + } + set { + _httpResponseBase.SubStatusCode = value; + } + } + + public override bool SuppressContent { + get { + return _httpResponseBase.SuppressContent; + } + set { + _httpResponseBase.SuppressContent = value; + } + } + + public override bool TrySkipIisCustomErrors { + get { + return _httpResponseBase.TrySkipIisCustomErrors; + } + set { + _httpResponseBase.TrySkipIisCustomErrors = value; + } + } + + } +} \ No newline at end of file diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index 5fb9af9b7..cba9e445b 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -163,6 +163,7 @@ + @@ -190,16 +191,22 @@ + + + ASPXCodeBehind + + +