From 4ea2ff7327fc8c4cade6d41253b4d6be83230d3b Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 19 Apr 2010 15:48:05 -0700 Subject: [PATCH 1/5] Adding comments to some environment model classes --HG-- branch : dev --- src/Orchard/Environment/Configuration/ShellSettings.cs | 5 +++++ .../Environment/Topology/Models/ShellDescriptor.cs | 7 +++++++ src/Orchard/Environment/Topology/Models/ShellTopology.cs | 9 ++++++--- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Orchard/Environment/Configuration/ShellSettings.cs b/src/Orchard/Environment/Configuration/ShellSettings.cs index b97d40653..7631d2ce0 100644 --- a/src/Orchard/Environment/Configuration/ShellSettings.cs +++ b/src/Orchard/Environment/Configuration/ShellSettings.cs @@ -1,4 +1,9 @@ namespace Orchard.Environment.Configuration { + /// + /// Represents the minimalistic set of fields stored for each tenant. This + /// model is obtained from the IShellSettingsManager, which by default reads this + /// from the App_Data settings.txt files. + /// public class ShellSettings { public string Name { get; set; } public string DataProvider { get; set; } diff --git a/src/Orchard/Environment/Topology/Models/ShellDescriptor.cs b/src/Orchard/Environment/Topology/Models/ShellDescriptor.cs index 9b9da6365..f03eb2b6d 100644 --- a/src/Orchard/Environment/Topology/Models/ShellDescriptor.cs +++ b/src/Orchard/Environment/Topology/Models/ShellDescriptor.cs @@ -2,6 +2,13 @@ using System.Linq; namespace Orchard.Environment.Topology.Models { + + /// + /// Contains a snapshot of a tenant's enabled features. + /// The information is drawn out of the shell via IShellDescriptorManager + /// and cached by the host via IShellDescriptorCache. It is + /// passed to the ICompositionStrategy to build the ShellTopology. + /// public class ShellDescriptor { public ShellDescriptor() { EnabledFeatures = Enumerable.Empty(); diff --git a/src/Orchard/Environment/Topology/Models/ShellTopology.cs b/src/Orchard/Environment/Topology/Models/ShellTopology.cs index 4cf51c77b..53989d5b6 100644 --- a/src/Orchard/Environment/Topology/Models/ShellTopology.cs +++ b/src/Orchard/Environment/Topology/Models/ShellTopology.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; -using Orchard.Environment.Configuration; -using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Topology.Models { + + /// + /// Contains the information necessary to initialize an IoC container + /// for a particular tenant. This model is created by the ICompositionStrategy + /// and is passed into the IShellContainerFactory. + /// public class ShellTopology { public IEnumerable Modules { get; set; } public IEnumerable Dependencies { get; set; } @@ -15,7 +19,6 @@ namespace Orchard.Environment.Topology.Models { public class ShellTopologyItem { public Type Type { get; set; } public Feature Feature { get; set; } - public FeatureDescriptor FeatureDescriptor { get; set; } } public class ModuleTopology : ShellTopologyItem { From 1800c632b6b82ddd2dc5687a368c650b1c6c4665 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 19 Apr 2010 16:21:20 -0700 Subject: [PATCH 2/5] Refactoring ShellTopology slightly Merging IModule and IDependency information in ShellTopology Avoids conceptual collision with Orchard Modules and Autofac IModule components --HG-- branch : dev --- .../DefaultCompositionStrategyTests.cs | 4 ++-- .../DefaultShellContainerFactoryTests.cs | 5 ++--- .../ShellBuilders/ShellContainerFactory.cs | 16 +++++++++++----- .../Environment/Topology/CompositionStrategy.cs | 16 ++++++++++------ .../Environment/Topology/Models/ShellTopology.cs | 4 ---- 5 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs b/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs index c7663ad77..a6d20e3f2 100644 --- a/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs +++ b/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs @@ -139,8 +139,8 @@ namespace Orchard.Tests.Environment { var compositionStrategy = _container.Resolve(); var topology = compositionStrategy.Compose(descriptor); - var alpha = topology.Modules.Single(x => x.Type == typeof (AlphaModule)); - var beta = topology.Modules.Single(x => x.Type == typeof (BetaModule)); + var alpha = topology.Dependencies.Single(x => x.Type == typeof (AlphaModule)); + var beta = topology.Dependencies.Single(x => x.Type == typeof(BetaModule)); Assert.That(alpha.Feature.Descriptor.Name, Is.EqualTo("Foo")); Assert.That(beta.Feature.Descriptor.Name, Is.EqualTo("Bar")); diff --git a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs index 3f9e8b39f..bbf204a10 100644 --- a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs +++ b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs @@ -34,15 +34,14 @@ namespace Orchard.Tests.Environment.ShellBuilders { } ShellTopology CreateTopology(params ShellTopologyItem[] items) { return new ShellTopology { - Modules = items.OfType(), Dependencies = items.OfType(), Controllers = items.OfType(), Records = items.OfType(), }; } - ModuleTopology WithModule() { - return new ModuleTopology { Type = typeof(T) }; + DependencyTopology WithModule() { + return new DependencyTopology { Type = typeof(T), Parameters = Enumerable.Empty() }; } ControllerTopology WithController(string areaName, string controllerName) { diff --git a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs index 520a55b8f..1774355aa 100644 --- a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs +++ b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs @@ -11,7 +11,7 @@ using Orchard.Environment.Configuration; using Orchard.Environment.Topology.Models; namespace Orchard.Environment.ShellBuilders { - + public interface IShellContainerFactory { ILifetimeScope CreateContainer(ShellSettings settings, ShellTopology topology); } @@ -26,10 +26,16 @@ namespace Orchard.Environment.ShellBuilders { public ILifetimeScope CreateContainer(ShellSettings settings, ShellTopology topology) { var intermediateScope = _lifetimeScope.BeginLifetimeScope( builder => { - foreach (var item in topology.Modules) { - RegisterType(builder, item) + foreach (var item in topology.Dependencies.Where(t => typeof(IModule).IsAssignableFrom(t.Type))) { + var registration = RegisterType(builder, item) .Keyed(item.Type) .InstancePerDependency(); + + foreach (var parameter in item.Parameters) { + registration = registration + .WithParameter(parameter.Name, parameter.Value) + .WithProperty(parameter.Name, parameter.Value); + } } }); @@ -43,11 +49,11 @@ namespace Orchard.Environment.ShellBuilders { builder.Register(ctx => topology); var moduleIndex = intermediateScope.Resolve>(); - foreach (var item in topology.Modules) { + foreach (var item in topology.Dependencies.Where(t => typeof(IModule).IsAssignableFrom(t.Type))) { builder.RegisterModule(moduleIndex[item.Type]); } - foreach (var item in topology.Dependencies) { + foreach (var item in topology.Dependencies.Where(t => typeof(IDependency).IsAssignableFrom(t.Type))) { var registration = RegisterType(builder, item) .EnableDynamicProxy(dynamicProxyContext) .InstancePerLifetimeScope(); diff --git a/src/Orchard/Environment/Topology/CompositionStrategy.cs b/src/Orchard/Environment/Topology/CompositionStrategy.cs index 038b5bc0b..47faa6c54 100644 --- a/src/Orchard/Environment/Topology/CompositionStrategy.cs +++ b/src/Orchard/Environment/Topology/CompositionStrategy.cs @@ -39,11 +39,15 @@ namespace Orchard.Environment.Topology { if (descriptor.EnabledFeatures.Any(feature => feature.Name == "Orchard.Framework")) features = features.Concat(BuiltinFeatures()); + var modules = BuildTopology(features, IsModule, BuildModule); + var dependencies = BuildTopology(features, IsDependency, (t, f) => BuildDependency(t, f, descriptor)); + var controllers = BuildTopology(features, IsController, BuildController); + var records = BuildTopology(features, IsRecord, BuildRecord); + return new ShellTopology { - Modules = BuildTopology(features, IsModule, BuildModule), - Dependencies = BuildTopology(features, IsDependency, (t, f) => BuildDependency(t, f, descriptor)), - Controllers = BuildTopology(features, IsController, BuildController), - Records = BuildTopology(features, IsRecord, BuildRecord), + Dependencies = dependencies.Concat(modules).ToArray(), + Controllers = controllers, + Records = records, }; } @@ -81,8 +85,8 @@ namespace Orchard.Environment.Topology { return typeof(IModule).IsAssignableFrom(type); } - private static ModuleTopology BuildModule(Type type, Feature feature) { - return new ModuleTopology { Type = type, Feature = feature }; + private static DependencyTopology BuildModule(Type type, Feature feature) { + return new DependencyTopology { Type = type, Feature = feature, Parameters = Enumerable.Empty() }; } private static bool IsDependency(Type type) { diff --git a/src/Orchard/Environment/Topology/Models/ShellTopology.cs b/src/Orchard/Environment/Topology/Models/ShellTopology.cs index 53989d5b6..557b8478b 100644 --- a/src/Orchard/Environment/Topology/Models/ShellTopology.cs +++ b/src/Orchard/Environment/Topology/Models/ShellTopology.cs @@ -10,7 +10,6 @@ namespace Orchard.Environment.Topology.Models { /// and is passed into the IShellContainerFactory. /// public class ShellTopology { - public IEnumerable Modules { get; set; } public IEnumerable Dependencies { get; set; } public IEnumerable Controllers { get; set; } public IEnumerable Records { get; set; } @@ -21,9 +20,6 @@ namespace Orchard.Environment.Topology.Models { public Feature Feature { get; set; } } - public class ModuleTopology : ShellTopologyItem { - } - public class DependencyTopology : ShellTopologyItem { public IEnumerable Parameters { get; set; } } From 78d5711e1afe1f7cda25a1af94299ba8af756d03 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 19 Apr 2010 16:34:35 -0700 Subject: [PATCH 3/5] Creating feature for extension name implicitly Supports existing module.txt file format Reduces the size of a minimalistic descriptor file --HG-- branch : dev --- .../Extensions/ExtensionManagerTests.cs | 18 ++++++++ src/Orchard.Web/Core/Settings/Module.txt | 3 -- .../Modules/Orchard.Setup/Module.txt | 3 -- .../Extensions/ExtensionManager.cs | 43 +++++++++++-------- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index cfd022b28..deb9796e3 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -295,5 +295,23 @@ features: Assert.That((type == typeof(Alpha) || (type == typeof(Beta)))); } } + + [Test] + public void ModuleNameIsIntroducedAsFeatureImplicitly() { + var extensionLoader = new StubLoaders(); + var extensionFolder = new StubFolders(); + + extensionFolder.Manifests.Add("Minimalistic", @" +name: Minimalistic +version: 1.0.3 +orchardversion: 1 +"); + + ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Name == "Minimalistic"); + + Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); + Assert.That(minimalisticModule.Features.Single().Name, Is.EqualTo("Minimalistic")); + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Settings/Module.txt b/src/Orchard.Web/Core/Settings/Module.txt index fe53403bc..feb256afe 100644 --- a/src/Orchard.Web/Core/Settings/Module.txt +++ b/src/Orchard.Web/Core/Settings/Module.txt @@ -1,5 +1,2 @@ name: Settings antiforgery: enabled -features: - Settings: - description: Site configuration support diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Module.txt b/src/Orchard.Web/Modules/Orchard.Setup/Module.txt index def8d82d7..df08e832d 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Setup/Module.txt @@ -1,5 +1,2 @@ name: Setup antiforgery: enabled -features: - Orchard.Setup: - description: Components needed to initialize a new instance or tenant diff --git a/src/Orchard/Environment/Extensions/ExtensionManager.cs b/src/Orchard/Environment/Extensions/ExtensionManager.cs index 5a4c06651..e0a6abf1f 100644 --- a/src/Orchard/Environment/Extensions/ExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/ExtensionManager.cs @@ -88,26 +88,33 @@ namespace Orchard.Environment.Extensions { private static IEnumerable GetFeaturesForExtension(Mapping features, ExtensionDescriptor extensionDescriptor) { List featureDescriptors = new List(); - if (features == null) return featureDescriptors; - foreach (var entity in features.Entities) { - FeatureDescriptor featureDescriptor = new FeatureDescriptor { - Extension = extensionDescriptor, - Name = entity.Key.ToString(), - }; - Mapping featureMapping = (Mapping)entity.Value; - foreach (var featureEntity in featureMapping.Entities) { - if (String.Equals(featureEntity.Key.ToString(), "description", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Description = featureEntity.Value.ToString(); - } - else if (String.Equals(featureEntity.Key.ToString(), "category", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Category = featureEntity.Value.ToString(); - } - else if (String.Equals(featureEntity.Key.ToString(), "dependencies", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Dependencies = ParseFeatureDependenciesEntry(featureEntity.Value.ToString()); + if (features != null) { + foreach (var entity in features.Entities) { + FeatureDescriptor featureDescriptor = new FeatureDescriptor { + Extension = extensionDescriptor, + Name = entity.Key.ToString(), + }; + Mapping featureMapping = (Mapping)entity.Value; + foreach (var featureEntity in featureMapping.Entities) { + if (String.Equals(featureEntity.Key.ToString(), "description", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Description = featureEntity.Value.ToString(); + } + else if (String.Equals(featureEntity.Key.ToString(), "category", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Category = featureEntity.Value.ToString(); + } + else if (String.Equals(featureEntity.Key.ToString(), "dependencies", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Dependencies = ParseFeatureDependenciesEntry(featureEntity.Value.ToString()); + } } + featureDescriptors.Add(featureDescriptor); } - featureDescriptors.Add(featureDescriptor); - + } + if (!featureDescriptors.Any(fd => fd.Name == extensionDescriptor.Name)) { + featureDescriptors.Add(new FeatureDescriptor { + Name = extensionDescriptor.Name, + Dependencies = new string[0], + Extension = extensionDescriptor, + }); } return featureDescriptors; } From ffa92f76e9db4f47980ff467bb60de691702bacc Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 19 Apr 2010 16:52:22 -0700 Subject: [PATCH 4/5] Passing along ShellSettings to apply DataPrefix to RecordTopology --HG-- branch : dev --- .../DefaultCompositionStrategyTests.cs | 80 ++++++++++++++----- .../DefaultShellContextFactoryTests.cs | 9 ++- src/Orchard/Environment/DefaultOrchardHost.cs | 4 +- .../ShellBuilders/ShellContextFactory.cs | 30 ++++--- .../Topology/CompositionStrategy.cs | 15 ++-- 5 files changed, 95 insertions(+), 43 deletions(-) diff --git a/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs b/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs index a6d20e3f2..dc964d96f 100644 --- a/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs +++ b/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs @@ -8,6 +8,7 @@ using Autofac.Core; using Moq; using NUnit.Framework; using Orchard.ContentManagement.Records; +using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Environment.Topology; @@ -51,16 +52,21 @@ namespace Orchard.Tests.Environment { }); } + private static ShellSettings BuildDefaultSettings() { + return new ShellSettings { Name = "Default" }; + } + [Test] public void TopologyIsNotNull() { var descriptor = Build.TopologyDescriptor(); var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); Assert.That(topology, Is.Not.Null); } + [Test] public void DependenciesFromFeatureArePutIntoTopology() { var descriptor = Build.TopologyDescriptor().WithFeatures("Foo", "Bar"); @@ -74,7 +80,7 @@ namespace Orchard.Tests.Environment { _featureTypes["Bar"] = new[] { typeof(BarService1) }; var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); Assert.That(topology, Is.Not.Null); Assert.That(topology.Dependencies.Count(), Is.EqualTo(2)); @@ -115,7 +121,7 @@ namespace Orchard.Tests.Environment { _featureTypes["Foo"] = new[] { typeof(FooService1) }; var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); var foo = topology.Dependencies.SingleOrDefault(t => t.Type == typeof(FooService1)); Assert.That(foo, Is.Not.Null); @@ -137,9 +143,9 @@ namespace Orchard.Tests.Environment { _featureTypes["Bar"] = new[] { typeof(BetaModule) }; var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); - var alpha = topology.Dependencies.Single(x => x.Type == typeof (AlphaModule)); + var alpha = topology.Dependencies.Single(x => x.Type == typeof(AlphaModule)); var beta = topology.Dependencies.Single(x => x.Type == typeof(BetaModule)); Assert.That(alpha.Feature.Descriptor.Name, Is.EqualTo("Foo")); @@ -170,11 +176,11 @@ namespace Orchard.Tests.Environment { _featureTypes["Bar Minus"] = new[] { typeof(DeltaController), typeof(EpsilonController) }; var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); - var gamma = topology.Controllers.Single(x => x.Type == typeof (GammaController)); - var delta = topology.Controllers.Single(x => x.Type == typeof (DeltaController)); - var epsilon = topology.Controllers.Single(x => x.Type == typeof (EpsilonController)); + var gamma = topology.Controllers.Single(x => x.Type == typeof(GammaController)); + var delta = topology.Controllers.Single(x => x.Type == typeof(DeltaController)); + var epsilon = topology.Controllers.Single(x => x.Type == typeof(EpsilonController)); Assert.That(gamma.Feature.Descriptor.Name, Is.EqualTo("Foo Plus")); Assert.That(gamma.AreaName, Is.EqualTo("MyCompany.Foo")); @@ -189,7 +195,7 @@ namespace Orchard.Tests.Environment { Assert.That(epsilon.ControllerName, Is.EqualTo("Epsilon")); } - + public class GammaController : Controller { } @@ -205,7 +211,7 @@ namespace Orchard.Tests.Environment { } } - + [Test] public void RecordsArePutIntoTopologyWithTableName() { var descriptor = Build.TopologyDescriptor().WithFeatures("Foo Plus", "Bar", "Bar Minus"); @@ -221,10 +227,10 @@ namespace Orchard.Tests.Environment { _featureTypes["Bar Minus"] = Enumerable.Empty(); var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); - var foo = topology.Records.Single(x => x.Type == typeof (FooRecord)); - var bar = topology.Records.Single(x => x.Type == typeof (BarRecord)); + var foo = topology.Records.Single(x => x.Type == typeof(FooRecord)); + var bar = topology.Records.Single(x => x.Type == typeof(BarRecord)); Assert.That(foo.Feature.Descriptor.Name, Is.EqualTo("Foo Plus")); Assert.That(foo.TableName, Is.EqualTo("MyCompany_Foo_FooRecord")); @@ -236,14 +242,14 @@ namespace Orchard.Tests.Environment { [Test] public void CoreRecordsAreAddedAutomatically() { var descriptor = Build.TopologyDescriptor().WithFeatures("Orchard.Framework"); - - var compositionStrategy = _container.Resolve(); - var topology = compositionStrategy.Compose(descriptor); - var ct = topology.Records.Single(x => x.Type == typeof (ContentTypeRecord)); - var ci = topology.Records.Single(x => x.Type == typeof (ContentItemRecord)); - var civ = topology.Records.Single(x => x.Type == typeof (ContentItemVersionRecord)); - + var compositionStrategy = _container.Resolve(); + var topology = compositionStrategy.Compose(BuildDefaultSettings(), descriptor); + + var ct = topology.Records.Single(x => x.Type == typeof(ContentTypeRecord)); + var ci = topology.Records.Single(x => x.Type == typeof(ContentItemRecord)); + var civ = topology.Records.Single(x => x.Type == typeof(ContentItemVersionRecord)); + Assert.That(ct.Feature.Descriptor.Name, Is.EqualTo("Orchard.Framework")); Assert.That(ct.TableName, Is.EqualTo("Orchard_Framework_ContentTypeRecord")); @@ -252,6 +258,36 @@ namespace Orchard.Tests.Environment { Assert.That(civ.Feature.Descriptor.Name, Is.EqualTo("Orchard.Framework")); Assert.That(civ.TableName, Is.EqualTo("Orchard_Framework_ContentItemVersionRecord")); - } + } + + [Test] + public void DataPrefixChangesTableName() { + var settings = BuildDefaultSettings(); + settings.DataPrefix = "Yadda"; + var descriptor = Build.TopologyDescriptor().WithFeatures("Foo Plus", "Bar", "Bar Minus"); + + _extensionDescriptors = new[] { + Build.ExtensionDescriptor("MyCompany.Foo", "Foo").WithFeatures("Foo", "Foo Plus"), + Build.ExtensionDescriptor("Bar").WithFeatures("Bar", "Bar Minus"), + }; + + _featureTypes["Foo"] = Enumerable.Empty(); + _featureTypes["Foo Plus"] = new[] { typeof(FooRecord) }; + _featureTypes["Bar"] = new[] { typeof(BarRecord) }; + _featureTypes["Bar Minus"] = Enumerable.Empty(); + + var compositionStrategy = _container.Resolve(); + var topology = compositionStrategy.Compose(settings, descriptor); + + var foo = topology.Records.Single(x => x.Type == typeof(FooRecord)); + var bar = topology.Records.Single(x => x.Type == typeof(BarRecord)); + + Assert.That(foo.Feature.Descriptor.Name, Is.EqualTo("Foo Plus")); + Assert.That(foo.TableName, Is.EqualTo("Yadda_MyCompany_Foo_FooRecord")); + + Assert.That(bar.Feature.Descriptor.Name, Is.EqualTo("Bar")); + Assert.That(bar.TableName, Is.EqualTo("Yadda_Bar_BarRecord")); + } + } } diff --git a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContextFactoryTests.cs b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContextFactoryTests.cs index c7f35ec0f..4ee775e37 100644 --- a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContextFactoryTests.cs +++ b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContextFactoryTests.cs @@ -34,7 +34,7 @@ namespace Orchard.Tests.Environment.ShellBuilders { .Returns(topologyDescriptor); _container.Mock() - .Setup(x => x.Compose(topologyDescriptor)) + .Setup(x => x.Compose(settings, topologyDescriptor)) .Returns(topology); _container.Mock() @@ -47,7 +47,7 @@ namespace Orchard.Tests.Environment.ShellBuilders { var factory = _container.Resolve(); - var context = factory.Create(settings); + var context = factory.CreateShellContext(settings); Assert.That(context.Settings, Is.SameAs(settings)); Assert.That(context.Descriptor, Is.SameAs(topologyDescriptor)); @@ -58,10 +58,11 @@ namespace Orchard.Tests.Environment.ShellBuilders { [Test] public void NullSettingsReturnsSetupContext() { + var settings = new ShellSettings { Name = "Default" }; var topology = new ShellTopology(); _container.Mock() - .Setup(x => x.Compose(It.IsAny())) + .Setup(x => x.Compose(settings, It.IsAny())) .Returns(topology); _container.Mock() @@ -69,7 +70,7 @@ namespace Orchard.Tests.Environment.ShellBuilders { .Returns(_container.BeginLifetimeScope("shell")); var factory = _container.Resolve(); - var context = factory.Create(null); + var context = factory.CreateShellContext(null); Assert.That(context.Descriptor.EnabledFeatures, Has.Some.With.Property("Name").EqualTo("Orchard.Setup")); } diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 5ea9f3374..40efcfd31 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -92,12 +92,12 @@ namespace Orchard.Environment { ShellContext CreateSetupContext() { Logger.Debug("Creating shell context for setup"); - return _shellContextFactory.Create(null); + return _shellContextFactory.CreateSetupContext(); } ShellContext CreateShellContext(ShellSettings settings) { Logger.Debug("Creating shell context for tenant {0}", settings.Name); - return _shellContextFactory.Create(settings); + return _shellContextFactory.CreateShellContext(settings); } protected virtual void BeginRequest() { diff --git a/src/Orchard/Environment/ShellBuilders/ShellContextFactory.cs b/src/Orchard/Environment/ShellBuilders/ShellContextFactory.cs index c6dedc78d..0c7546c87 100644 --- a/src/Orchard/Environment/ShellBuilders/ShellContextFactory.cs +++ b/src/Orchard/Environment/ShellBuilders/ShellContextFactory.cs @@ -6,9 +6,21 @@ using Orchard.Environment.Topology.Models; using Orchard.Logging; namespace Orchard.Environment.ShellBuilders { - + /// + /// High-level coordinator that exercises other component capabilities to + /// build all of the artifacts for a running shell given a tenant settings. + /// public interface IShellContextFactory { - ShellContext Create(ShellSettings settings); + /// + /// Builds a shell context given a specific tenant settings structure + /// + ShellContext CreateShellContext(ShellSettings settings); + + /// + /// Builds a shell context for an uninitialized Orchard instance. Needed + /// to display setup user interface. + /// + ShellContext CreateSetupContext(); } public class ShellContextFactory : IShellContextFactory { @@ -28,10 +40,7 @@ namespace Orchard.Environment.ShellBuilders { public ILogger Logger { get; set; } - public ShellContext Create(ShellSettings settings) { - if (settings == null) { - return CreateSetupContext(); - } + public ShellContext CreateShellContext(ShellSettings settings) { Logger.Debug("Creating shell context for tenant {0}", settings.Name); @@ -41,7 +50,7 @@ namespace Orchard.Environment.ShellBuilders { knownDescriptor = MinimumTopologyDescriptor(); } - var topology = _compositionStrategy.Compose(knownDescriptor); + var topology = _compositionStrategy.Compose(settings, knownDescriptor); var shellScope = _shellContainerFactory.CreateContainer(settings, topology); ShellDescriptor currentDescriptor; @@ -54,7 +63,7 @@ namespace Orchard.Environment.ShellBuilders { Logger.Information("Newer topology obtained. Rebuilding shell container."); _shellDescriptorCache.Store(settings.Name, currentDescriptor); - topology = _compositionStrategy.Compose(currentDescriptor); + topology = _compositionStrategy.Compose(settings, currentDescriptor); shellScope = _shellContainerFactory.CreateContainer(settings, topology); } @@ -78,16 +87,17 @@ namespace Orchard.Environment.ShellBuilders { }; } - private ShellContext CreateSetupContext() { + public ShellContext CreateSetupContext() { Logger.Warning("No shell settings available. Creating shell context for setup"); var settings = new ShellSettings { Name = "__Orchard__Setup__" }; + var descriptor = new ShellDescriptor { SerialNumber = -1, EnabledFeatures = new[] { new ShellFeature { Name = "Orchard.Setup" } }, }; - var topology = _compositionStrategy.Compose(descriptor); + var topology = _compositionStrategy.Compose(settings, descriptor); var shellScope = _shellContainerFactory.CreateContainer(settings, topology); return new ShellContext { diff --git a/src/Orchard/Environment/Topology/CompositionStrategy.cs b/src/Orchard/Environment/Topology/CompositionStrategy.cs index 47faa6c54..c7f7f85ba 100644 --- a/src/Orchard/Environment/Topology/CompositionStrategy.cs +++ b/src/Orchard/Environment/Topology/CompositionStrategy.cs @@ -6,6 +6,7 @@ using System.Web.Mvc; using Autofac.Core; using Orchard.ContentManagement; using Orchard.ContentManagement.Records; +using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Environment.Topology.Models; @@ -19,7 +20,7 @@ namespace Orchard.Environment.Topology { /// Using information from the IExtensionManager, transforms and populates all of the /// topology model the shell builders will need to correctly initialize a tenant IoC container. /// - ShellTopology Compose(ShellDescriptor descriptor); + ShellTopology Compose(ShellSettings settings, ShellDescriptor descriptor); } public class CompositionStrategy : ICompositionStrategy { @@ -29,7 +30,7 @@ namespace Orchard.Environment.Topology { _extensionManager = extensionManager; } - public ShellTopology Compose(ShellDescriptor descriptor) { + public ShellTopology Compose(ShellSettings settings, ShellDescriptor descriptor) { var enabledFeatures = _extensionManager.AvailableExtensions() .SelectMany(extensionDescriptor => extensionDescriptor.Features) .Where(featureDescriptor => IsFeatureEnabledInTopology(featureDescriptor, descriptor)); @@ -42,7 +43,7 @@ namespace Orchard.Environment.Topology { var modules = BuildTopology(features, IsModule, BuildModule); var dependencies = BuildTopology(features, IsDependency, (t, f) => BuildDependency(t, f, descriptor)); var controllers = BuildTopology(features, IsController, BuildController); - var records = BuildTopology(features, IsRecord, BuildRecord); + var records = BuildTopology(features, IsRecord, (t, f) => BuildRecord(t, f, settings)); return new ShellTopology { Dependencies = dependencies.Concat(modules).ToArray(), @@ -129,14 +130,18 @@ namespace Orchard.Environment.Topology { (!typeof(IContent).IsAssignableFrom(type) || typeof(ContentPartRecord).IsAssignableFrom(type)); } - private static RecordTopology BuildRecord(Type type, Feature feature) { + private static RecordTopology BuildRecord(Type type, Feature feature, ShellSettings settings) { var extensionDescriptor = feature.Descriptor.Extension; var extensionName = extensionDescriptor.Name.Replace('.', '_'); + var dataPrefix = ""; + if (!string.IsNullOrEmpty(settings.DataPrefix)) + dataPrefix = settings.DataPrefix + "_"; + return new RecordTopology { Type = type, Feature = feature, - TableName = extensionName + '_' + type.Name, + TableName = dataPrefix + extensionName + '_' + type.Name, }; } } From fc6acf68e150680b3d158a207fb20b04e879d99f Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Mon, 19 Apr 2010 18:29:45 -0700 Subject: [PATCH 5/5] Fixing an issue with order-of-execution in setup Database created and schema generated before the rest of the setup info is applied Known set of minimum modules are used to generate schema, and are saved in the shell's records --HG-- branch : dev --- .../Topology/Records/TopologyRecord.cs | 5 ++ .../Controllers/SetupController.cs | 62 ++++++++++++++----- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/Orchard.Web/Core/Settings/Topology/Records/TopologyRecord.cs b/src/Orchard.Web/Core/Settings/Topology/Records/TopologyRecord.cs index daf6df51a..b3333fb83 100644 --- a/src/Orchard.Web/Core/Settings/Topology/Records/TopologyRecord.cs +++ b/src/Orchard.Web/Core/Settings/Topology/Records/TopologyRecord.cs @@ -1,10 +1,15 @@ using System.Collections.Generic; +using Orchard.Data.Conventions; namespace Orchard.Core.Settings.Topology.Records { public class TopologyRecord { public virtual int Id { get; set; } public virtual int SerialNumber { get; set; } + + [CascadeAllDeleteOrphan] public virtual IList EnabledFeatures { get; set; } + + [CascadeAllDeleteOrphan] public virtual IList Parameters { get; set; } } } diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs index 6c65b77a8..fd4eab673 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Web.Mvc; using Orchard.Comments.Models; using Orchard.ContentManagement; @@ -9,6 +10,9 @@ using Orchard.Data; using Orchard.Environment; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; +using Orchard.Environment.ShellBuilders; +using Orchard.Environment.Topology; +using Orchard.Environment.Topology.Models; using Orchard.Security; using Orchard.Settings; using Orchard.Setup.ViewModels; @@ -22,16 +26,22 @@ namespace Orchard.Setup.Controllers { private readonly INotifier _notifier; private readonly IOrchardHost _orchardHost; private readonly IShellSettingsManager _shellSettingsManager; + private readonly IShellContainerFactory _shellContainerFactory; + private readonly ICompositionStrategy _compositionStrategy; private readonly IAppDataFolder _appDataFolder; public SetupController( INotifier notifier, - IOrchardHost orchardHost, + IOrchardHost orchardHost, IShellSettingsManager shellSettingsManager, + IShellContainerFactory shellContainerFactory, + ICompositionStrategy compositionStrategy, IAppDataFolder appDataFolder) { _notifier = notifier; _orchardHost = orchardHost; _shellSettingsManager = shellSettingsManager; + _shellContainerFactory = shellContainerFactory; + _compositionStrategy = compositionStrategy; _appDataFolder = appDataFolder; T = NullLocalizer.Instance; } @@ -71,26 +81,44 @@ namespace Orchard.Setup.Controllers { DataConnectionString = model.DatabaseConnectionString }; + const string hardcoded = @"Orchard.Framework, + Common,Dashboard,Feeds,HomePage,Navigation,Scheduling,Settings,Themes,XmlRpc, + Orchard.Users,Orchard.Roles,TinyMce, + Orchard.Pages,Orchard.Comments"; + + var shellDescriptor = new ShellDescriptor { + EnabledFeatures = hardcoded.Split(',').Select(name => new ShellFeature { Name = name.Trim() }) + }; + + var shellToplogy = _compositionStrategy.Compose(shellSettings, shellDescriptor); + + // initialize database explicitly, and store shell descriptor + var bootstrapLifetimeScope = _shellContainerFactory.CreateContainer(shellSettings, shellToplogy); + using (var environment = new StandaloneEnvironment(bootstrapLifetimeScope)) { + environment.Resolve().CreateDatabase(); + + environment.Resolve().UpdateShellDescriptor( + 0, + shellDescriptor.EnabledFeatures, + shellDescriptor.Parameters); + } + + // creating a standalone environment. // in theory this environment can be used to resolve any normal components by interface, and those // components will exist entirely in isolation - no crossover between the safemode container currently in effect - using (var finiteEnvironment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { + using (var environment = _orchardHost.CreateStandaloneEnvironment(shellSettings)) { try { - // initialize database before the transaction is created - var sessionFactoryHolder = finiteEnvironment.Resolve(); - sessionFactoryHolder.CreateDatabase(); - - // create superuser - var membershipService = finiteEnvironment.Resolve(); + var membershipService = environment.Resolve(); var user = membershipService.CreateUser(new CreateUserParams(model.AdminUsername, model.AdminPassword, String.Empty, String.Empty, String.Empty, true)); - + // set site name and settings - var siteService = finiteEnvironment.Resolve(); + var siteService = environment.Resolve(); var siteSettings = siteService.GetSiteSettings().As(); siteSettings.Record.SiteSalt = Guid.NewGuid().ToString("N"); siteSettings.Record.SiteName = model.SiteName; @@ -98,13 +126,13 @@ namespace Orchard.Setup.Controllers { siteSettings.Record.PageTitleSeparator = " - "; // set site theme - var themeService = finiteEnvironment.Resolve(); + var themeService = environment.Resolve(); themeService.SetSiteTheme("Classic"); - var contentManager = finiteEnvironment.Resolve(); + var contentManager = environment.Resolve(); // simulate installation-time module activation events - var hackInstallationGenerator = finiteEnvironment.Resolve(); + var hackInstallationGenerator = environment.Resolve(); hackInstallationGenerator.GenerateInstallEvents(); // create home page as a CMS page @@ -124,12 +152,12 @@ namespace Orchard.Setup.Controllers { menuItem.As().OnMainMenu = true; menuItem.As().Url = ""; - var authenticationService = finiteEnvironment.Resolve(); + var authenticationService = environment.Resolve(); authenticationService.SignIn(user, true); - + } catch { - finiteEnvironment.Resolve().Cancel(); + environment.Resolve().Cancel(); throw; } } @@ -143,7 +171,7 @@ namespace Orchard.Setup.Controllers { } catch (Exception exception) { _notifier.Error(T("Setup failed:")); - for(var scan = exception; scan !=null; scan = scan.InnerException){ + for (var scan = exception; scan != null; scan = scan.InnerException) { _notifier.Error(scan.Message); } return IndexViewResult(model);