diff --git a/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs b/src/Orchard.Tests/Environment/DefaultCompositionStrategyTests.cs index c7663ad77..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,10 +143,10 @@ 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.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")); @@ -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/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.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.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.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/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); 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/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/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/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; } 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/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 038b5bc0b..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)); @@ -39,11 +40,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, (t, f) => BuildRecord(t, f, settings)); + 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 +86,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) { @@ -125,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, }; } } 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 9e99ff500..4088e9aee 100644 --- a/src/Orchard/Environment/Topology/Models/ShellTopology.cs +++ b/src/Orchard/Environment/Topology/Models/ShellTopology.cs @@ -1,15 +1,15 @@ using System; using System.Collections.Generic; -using Orchard.Environment.Configuration; -using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; namespace Orchard.Environment.Topology.Models { + /// - /// The topology of the shell (humor) + /// 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; } public IEnumerable Controllers { get; set; } public IEnumerable Records { get; set; } @@ -20,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; } }