Merge pull request #5634 from OrchardCMS/feature/auto-enable-dependencies

Auto enable dependencies

Fixes #3132
This commit is contained in:
Sébastien Ros
2015-08-26 15:47:02 -07:00
34 changed files with 233 additions and 12279 deletions

View File

@@ -25,7 +25,6 @@ using Orchard.Tests.Stubs;
using Orchard.Tests.Utility;
using Orchard.WebApi.Routes;
using IModelBinderProvider = Orchard.Mvc.ModelBinders.IModelBinderProvider;
using Orchard.Tasks;
namespace Orchard.Tests.Environment {
[TestFixture]
@@ -139,8 +138,6 @@ namespace Orchard.Tests.Environment {
}
}
[Test, Ignore("containers are disposed when calling BeginRequest, maybe by the StubVirtualPathMonitor")]
public void NormalDependenciesShouldBeUniquePerRequestContainer() {
var host = _lifetime.Resolve<IOrchardHost>();

View File

@@ -206,7 +206,7 @@ Features:
Category: Content types
AnotherWiki Editor:
Description: A rich editor for wiki contents.
Dependencies: TinyMCE, AnotherWiki
Dependencies: TinyMce, AnotherWiki
Category: Input methods
AnotherWiki DistributionList:
Description: Sends e-mail alerts when wiki contents gets published.
@@ -241,7 +241,7 @@ Features:
Assert.That(featureDescriptor.Description, Is.EqualTo("A rich editor for wiki contents."));
Assert.That(featureDescriptor.Category, Is.EqualTo("Input methods"));
Assert.That(featureDescriptor.Dependencies.Count(), Is.EqualTo(2));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMCE"));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMce"));
Assert.That(featureDescriptor.Dependencies.Contains("AnotherWiki"));
break;
case "AnotherWiki DistributionList":
@@ -287,7 +287,7 @@ Category: Content types
Features:
AnotherWiki Editor:
Description: A rich editor for wiki contents.
Dependencies: TinyMCE, AnotherWiki
Dependencies: TinyMce, AnotherWiki
Category: Input methods
AnotherWiki DistributionList:
Description: Sends e-mail alerts when wiki contents gets published.
@@ -323,7 +323,7 @@ Features:
Assert.That(featureDescriptor.Description, Is.EqualTo("A rich editor for wiki contents."));
Assert.That(featureDescriptor.Category, Is.EqualTo("Input methods"));
Assert.That(featureDescriptor.Dependencies.Count(), Is.EqualTo(2));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMCE"));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMce"));
Assert.That(featureDescriptor.Dependencies.Contains("AnotherWiki"));
break;
case "AnotherWiki DistributionList":

View File

@@ -207,7 +207,7 @@ Features:
Category: Content types
AnotherWiki Editor:
Description: A rich editor for wiki contents.
Dependencies: TinyMCE, AnotherWiki
Dependencies: TinyMce, AnotherWiki
Category: Input methods
AnotherWiki DistributionList:
Description: Sends e-mail alerts when wiki contents gets published.
@@ -243,7 +243,7 @@ Features:
Assert.That(featureDescriptor.Description, Is.EqualTo("A rich editor for wiki contents."));
Assert.That(featureDescriptor.Category, Is.EqualTo("Input methods"));
Assert.That(featureDescriptor.Dependencies.Count(), Is.EqualTo(2));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMCE"));
Assert.That(featureDescriptor.Dependencies.Contains("TinyMce"));
Assert.That(featureDescriptor.Dependencies.Contains("AnotherWiki"));
break;
case "AnotherWiki DistributionList":

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Autofac;
using Moq;
using NUnit.Framework;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Environment.ShellBuilders;
using Orchard.Tests.Environment.TestDependencies;
using Orchard.Utility.Extensions;
namespace Orchard.Tests.Environment.ShellBuilders {
[TestFixture]
public class CompositionStrategyTests : ContainerTestBase {
private CompositionStrategy _compositionStrategy;
private Mock<IExtensionManager> _extensionManager;
protected override void Register(ContainerBuilder builder) {
_extensionManager = new Mock<IExtensionManager>(MockBehavior.Loose);
builder.RegisterType<CompositionStrategy>().AsSelf();
builder.RegisterInstance(_extensionManager.Object);
}
protected override void Resolve(ILifetimeScope container) {
_compositionStrategy = container.Resolve<CompositionStrategy>();
var alphaExtension = new ExtensionDescriptor {
Id = "Alpha",
Name = "Alpha",
ExtensionType = "Module"
};
var alphaFeatureDescriptor = new FeatureDescriptor {
Id = "Alpha",
Name = "Alpha",
Extension = alphaExtension
};
var betaFeatureDescriptor = new FeatureDescriptor {
Id = "Beta",
Name = "Beta",
Extension = alphaExtension,
Dependencies = new List<string> {
"Alpha"
}
};
alphaExtension.Features = new List<FeatureDescriptor> {
alphaFeatureDescriptor,
betaFeatureDescriptor
};
var features = new List<Feature> {
new Feature {
Descriptor = alphaFeatureDescriptor,
ExportedTypes = new List<Type> {
typeof(AlphaDependency)
}
},
new Feature {
Descriptor = betaFeatureDescriptor,
ExportedTypes = new List<Type> {
typeof(BetaDependency)
}
}
};
_extensionManager.Setup(x => x.AvailableExtensions()).Returns(new List<ExtensionDescriptor> {
alphaExtension
});
_extensionManager.Setup(x => x.AvailableFeatures()).Returns(
_extensionManager.Object.AvailableExtensions()
.SelectMany(ext => ext.Features)
.ToReadOnlyCollection());
_extensionManager.Setup(x => x.LoadFeatures(It.IsAny<IEnumerable<FeatureDescriptor>>())).Returns(features);
}
[Test]
public void ComposeReturnsBlueprintWithExpectedDependencies() {
var shellSettings = CreateShell();
var shellDescriptor = CreateShellDescriptor("Alpha", "Beta");
var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof (AlphaDependency)), Is.EqualTo(1));
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof(BetaDependency)), Is.EqualTo(1));
}
[Test]
public void ComposeReturnsBlueprintWithAutoEnabledDependencyFeatures() {
var shellSettings = CreateShell();
var shellDescriptor = CreateShellDescriptor("Beta"); // Beta has a dependency on Alpha, but is not enabled initially.
var shellBlueprint = _compositionStrategy.Compose(shellSettings, shellDescriptor);
Assert.That(shellBlueprint.Dependencies.Count(x => x.Type == typeof(AlphaDependency)), Is.EqualTo(1));
Assert.That(shellDescriptor.Features.Count(x => x.Name == "Alpha"), Is.EqualTo(1));
}
private ShellSettings CreateShell() {
return new ShellSettings();
}
private ShellDescriptor CreateShellDescriptor(params string[] enabledFeatures) {
var shellDescriptor = new ShellDescriptor {
Features = enabledFeatures.Select(x => new ShellFeature {
Name = x
})
};
return shellDescriptor;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Orchard.Tests.Environment.TestDependencies {
public interface IAlphaDependency : IDependency {
}
public class AlphaDependency : IAlphaDependency {
}
}

View File

@@ -0,0 +1,13 @@
namespace Orchard.Tests.Environment.TestDependencies {
public interface IBetaDependency : IDependency {
}
public class BetaDependency : IBetaDependency {
public IAlphaDependency Alpha { get; set; }
public BetaDependency(IAlphaDependency alpha) {
Alpha = alpha;
}
}
}

View File

@@ -245,9 +245,12 @@
<Compile Include="Environment\Extensions\ExtensionLoaderCoordinatorTests.cs" />
<Compile Include="Environment\Features\FeatureManagerTests.cs" />
<Compile Include="Environment\Loaders\DynamicExtensionLoaderTests.cs" />
<Compile Include="Environment\ShellBuilders\CompositionStrategyTests.cs" />
<Compile Include="Environment\State\DefaultProcessingEngineTests.cs" />
<Compile Include="Environment\RunningShellTableTests.cs" />
<Compile Include="Environment\StubHostEnvironment.cs" />
<Compile Include="Environment\TestDependencies\AlphaDependency.cs" />
<Compile Include="Environment\TestDependencies\BetaDependency.cs" />
<Compile Include="Environment\Utility\Build.cs" />
<Compile Include="Environment\Warmup\WarmupUtilityTests.cs" />
<Compile Include="FileSystems\AppData\AppDataFolderTests.cs" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
!function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery","./jquery.fileupload"],e):e(window.jQuery)}(function(e){"use strict";var s=e.blueimp.fileupload.prototype.options.add;e.widget("blueimp.fileupload",e.blueimp.fileupload,{options:{processQueue:[],add:function(r,i){var o=e(this);i.process(function(){return o.fileupload("process",i)}),s.call(this,r,i)}},processActions:{},_processFile:function(s,r){var i=this,o=e.Deferred().resolveWith(i,[s]),t=o.promise();return this._trigger("process",null,s),e.each(s.processQueue,function(s,o){var n=function(s){return r.errorThrown?e.Deferred().rejectWith(i,[r]).promise():i.processActions[o.action].call(i,s,o)};t=t.pipe(n,o.always&&n)}),t.done(function(){i._trigger("processdone",null,s),i._trigger("processalways",null,s)}).fail(function(){i._trigger("processfail",null,s),i._trigger("processalways",null,s)}),t},_transformProcessQueue:function(s){var r=[];e.each(s.processQueue,function(){var i={},o=this.action,t=this.prefix===!0?o:this.prefix;e.each(this,function(r,o){i[r]="string"===e.type(o)&&"@"===o.charAt(0)?s[o.slice(1)||(t?t+r.charAt(0).toUpperCase()+r.slice(1):r)]:o}),r.push(i)}),s.processQueue=r},processing:function(){return this._processing},process:function(s){var r=this,i=e.extend({},this.options,s);return i.processQueue&&i.processQueue.length&&(this._transformProcessQueue(i),0===this._processing&&this._trigger("processstart"),e.each(s.files,function(o){var t=o?e.extend({},i):i,n=function(){return s.errorThrown?e.Deferred().rejectWith(r,[s]).promise():r._processFile(t,s)};t.index=o,r._processing+=1,r._processingQueue=r._processingQueue.pipe(n,n).always(function(){r._processing-=1,0===r._processing&&r._trigger("processstop")})})),this._processingQueue},_create:function(){this._super(),this._processing=0,this._processingQueue=e.Deferred().resolveWith(this).promise()}})});

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
!function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery","./jquery.fileupload-process"],e):e(window.jQuery)}(function(e){"use strict";e.blueimp.fileupload.prototype.options.processQueue.push({action:"validate",always:!0,acceptFileTypes:"@",maxFileSize:"@",minFileSize:"@",maxNumberOfFiles:"@",disabled:"@disableValidation"}),e.widget("blueimp.fileupload",e.blueimp.fileupload,{options:{getNumberOfFiles:e.noop,messages:{maxNumberOfFiles:"Maximum number of files exceeded",acceptFileTypes:"File type not allowed",maxFileSize:"File is too large",minFileSize:"File is too small"}},processActions:{validate:function(i,l){if(l.disabled)return i;var r,s=e.Deferred(),t=this.options,o=i.files[i.index];return(l.minFileSize||l.maxFileSize)&&(r=o.size),"number"===e.type(l.maxNumberOfFiles)&&(t.getNumberOfFiles()||0)+i.files.length>l.maxNumberOfFiles?o.error=t.i18n("maxNumberOfFiles"):!l.acceptFileTypes||l.acceptFileTypes.test(o.type)||l.acceptFileTypes.test(o.name)?r>l.maxFileSize?o.error=t.i18n("maxFileSize"):"number"===e.type(r)&&r<l.minFileSize?o.error=t.i18n("minFileSize"):delete o.error:o.error=t.i18n("acceptFileTypes"),o.error||i.files.error?(i.files.error=!0,s.rejectWith(this,[i])):s.resolveWith(this,[i]),s.promise()}}})});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +0,0 @@
--- CommandBackgroundService.cs
+++ CommandBackgroundService.cs
@@ -1,7 +1,7 @@
using Orchard.Tasks;
namespace Orchard.Commands {
- public class CommandBackgroundService : IBackgroundService {
+ class CommandBackgroundService : IBackgroundService {
public void Sweep() {
// Don't run any background service in command line
}

View File

@@ -15,7 +15,7 @@ using Orchard.Logging;
using Orchard.Utility.Extensions;
namespace Orchard.Environment {
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter
// All the event handlers that DefaultOrchardHost implements have to be declared in OrchardStarter.
public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler {
private readonly IHostLocalRestart _hostLocalRestart;
private readonly IShellSettingsManager _shellSettingsManager;

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autofac.Features.OwnedInstances;
using Microsoft.Owin.Builder;
using Orchard.Environment.Configuration;
@@ -12,7 +11,6 @@ using Orchard.Owin;
using Orchard.Tasks;
using Orchard.UI;
using Orchard.WebApi.Routes;
using Owin;
using IModelBinderProvider = Orchard.Mvc.ModelBinders.IModelBinderProvider;
namespace Orchard.Environment {
@@ -37,6 +35,7 @@ namespace Orchard.Environment {
ISweepGenerator sweepGenerator,
IEnumerable<IOwinMiddlewareProvider> owinMiddlewareProviders,
ShellSettings shellSettings) {
_eventsFactory = eventsFactory;
_routeProviders = routeProviders;
_httpRouteProviders = httpRouteProviders;
@@ -53,7 +52,7 @@ namespace Orchard.Environment {
public ILogger Logger { get; set; }
public void Activate() {
IAppBuilder appBuilder = new AppBuilder();
var appBuilder = new AppBuilder();
appBuilder.Properties["host.AppName"] = _shellSettings.Name;
var orderedMiddlewares = _owinMiddlewareProviders
@@ -64,11 +63,10 @@ namespace Orchard.Environment {
middleware.Configure(appBuilder);
}
// register the Orchard middleware after all others
// Register the Orchard middleware after all others.
appBuilder.UseOrchard();
Func<IDictionary<string, object>, Task> pipeline = appBuilder.Build();
var pipeline = appBuilder.Build();
var allRoutes = new List<RouteDescriptor>();
allRoutes.AddRange(_routeProviders.SelectMany(provider => provider.GetRoutes()));
allRoutes.AddRange(_httpRouteProviders.SelectMany(provider => provider.GetRoutes()));
@@ -79,16 +77,17 @@ namespace Orchard.Environment {
using (var events = _eventsFactory()) {
events.Value.Activated();
}
_sweepGenerator.Activate();
}
public void Terminate() {
SafelyTerminate(() => {
using (var events = _eventsFactory()) {
SafelyTerminate(() => events.Value.Terminating());
}
});
using (var events = _eventsFactory()) {
var localEvents = events;
SafelyTerminate(() => localEvents.Value.Terminating());
}
});
SafelyTerminate(() => _sweepGenerator.Terminate());
}

View File

@@ -16,7 +16,7 @@ namespace Orchard.Environment.Descriptor {
/// <summary>
/// Alters databased information to match information passed as arguments.
/// Prior SerialNumber used for optomistic concurrency, and an exception
/// Prior SerialNumber used for optimistic concurrency, and an exception
/// should be thrown if the number in storage doesn't match what's provided.
/// </summary>
void UpdateShellDescriptor(

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions.Models;
namespace Orchard.Environment.Extensions.Helpers {
public static class ExtensionManagerExtensions {
public static IEnumerable<FeatureDescriptor> EnabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) {
return extensionManager.AvailableFeatures().Where(fd => descriptor.Features.Any(sf => sf.Name == fd.Id));
}
public static IEnumerable<FeatureDescriptor> DisabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) {
return extensionManager.AvailableFeatures().Where(fd => descriptor.Features.All(sf => sf.Name != fd.Id));
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.Extensions.Models;
namespace Orchard.Environment.Extensions {
@@ -10,4 +12,18 @@ namespace Orchard.Environment.Extensions {
IEnumerable<Feature> LoadFeatures(IEnumerable<FeatureDescriptor> featureDescriptors);
}
public static class ExtensionManagerExtensions {
public static IEnumerable<FeatureDescriptor> EnabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) {
return EnabledFeatures(extensionManager, descriptor.Features);
}
public static IEnumerable<FeatureDescriptor> EnabledFeatures(this IExtensionManager extensionManager, IEnumerable<ShellFeature> features) {
return extensionManager.AvailableFeatures().Where(fd => features.Any(sf => sf.Name == fd.Id));
}
public static IEnumerable<FeatureDescriptor> DisabledFeatures(this IExtensionManager extensionManager, ShellDescriptor descriptor) {
return extensionManager.AvailableFeatures().Where(fd => descriptor.Features.All(sf => sf.Name != fd.Id));
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Mvc;
using Autofac.Core;
@@ -16,17 +15,6 @@ using Orchard.Environment.ShellBuilders.Models;
using Orchard.Logging;
namespace Orchard.Environment.ShellBuilders {
/// <summary>
/// Service at the host level to transform the cachable descriptor into the loadable blueprint.
/// </summary>
public interface ICompositionStrategy {
/// <summary>
/// Using information from the IExtensionManager, transforms and populates all of the
/// blueprint model the shell builders will need to correctly initialize a tenant IoC container.
/// </summary>
ShellBlueprint Compose(ShellSettings settings, ShellDescriptor descriptor);
}
public class CompositionStrategy : ICompositionStrategy {
private readonly IExtensionManager _extensionManager;
@@ -41,14 +29,19 @@ namespace Orchard.Environment.ShellBuilders {
public ShellBlueprint Compose(ShellSettings settings, ShellDescriptor descriptor) {
Logger.Debug("Composing blueprint");
var enabledFeatures = _extensionManager.EnabledFeatures(descriptor);
var features = _extensionManager.LoadFeatures(enabledFeatures);
var builtinFeatures = BuiltinFeatures().ToList();
var builtinFeatureDescriptors = builtinFeatures.Select(x => x.Descriptor).ToList();
var availableFeatures = _extensionManager.AvailableFeatures().Concat(builtinFeatureDescriptors).ToDictionary(x => x.Id);
var enabledFeatures = _extensionManager.EnabledFeatures(descriptor).Select(x => x.Id).ToList();
var expandedFeatures = ExpandDependencies(availableFeatures, descriptor.Features.Select(x => x.Name)).ToList();
var autoEnabledDependencyFeatures = expandedFeatures.Except(enabledFeatures).Except(builtinFeatureDescriptors.Select(x => x.Id)).ToList();
var featureDescriptors = _extensionManager.EnabledFeatures(expandedFeatures.Select(x => new ShellFeature { Name = x})).ToList();
var features = _extensionManager.LoadFeatures(featureDescriptors);
if (descriptor.Features.Any(feature => feature.Name == "Orchard.Framework"))
features = BuiltinFeatures().Concat(features);
features = builtinFeatures.Concat(features);
var excludedTypes = GetExcludedTypes(features);
var modules = BuildBlueprint(features, IsModule, BuildModule, excludedTypes);
var dependencies = BuildBlueprint(features, IsDependency, (t, f) => BuildDependency(t, f, descriptor), excludedTypes);
var controllers = BuildBlueprint(features, IsController, BuildController, excludedTypes);
@@ -64,16 +57,41 @@ namespace Orchard.Environment.ShellBuilders {
Records = records,
};
Logger.Debug("Done composing blueprint");
Logger.Debug("Done composing blueprint.");
if (autoEnabledDependencyFeatures.Any()) {
// Add any dependencies previously not enabled to the shell descriptor.
descriptor.Features = descriptor.Features.Concat(autoEnabledDependencyFeatures.Select(x => new ShellFeature { Name = x })).ToList();
Logger.Information("Automatically enabled the following dependency features: {0}.", String.Join(", ", autoEnabledDependencyFeatures));
}
return result;
}
private IEnumerable<string> ExpandDependencies(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features) {
return ExpandDependenciesInternal(availableFeatures, features).Distinct();
}
private IEnumerable<string> ExpandDependenciesInternal(IDictionary<string, FeatureDescriptor> availableFeatures, IEnumerable<string> features) {
foreach (var shellFeature in features) {
var feature = availableFeatures[shellFeature];
foreach (var childDependency in ExpandDependenciesInternal(availableFeatures, feature.Dependencies))
yield return childDependency;
foreach (var dependency in feature.Dependencies)
yield return dependency;
yield return shellFeature;
}
}
private static IEnumerable<string> GetExcludedTypes(IEnumerable<Feature> features) {
var excludedTypes = new HashSet<string>();
// Identify replaced types
foreach (Feature feature in features) {
foreach (Type type in feature.ExportedTypes) {
// Identify replaced types.
foreach (var feature in features) {
foreach (var type in feature.ExportedTypes) {
foreach (OrchardSuppressDependencyAttribute replacedType in type.GetCustomAttributes(typeof(OrchardSuppressDependencyAttribute), false)) {
excludedTypes.Add(replacedType.FullName);
}
@@ -160,7 +178,7 @@ namespace Orchard.Environment.ShellBuilders {
private static bool IsRecord(Type type) {
return ((type.Namespace ?? "").EndsWith(".Models") || (type.Namespace ?? "").EndsWith(".Records")) &&
type.GetProperty("Id") != null &&
(type.GetProperty("Id").GetAccessors() ?? Enumerable.Empty<MethodInfo>()).All(x => x.IsVirtual) &&
(type.GetProperty("Id").GetAccessors()).All(x => x.IsVirtual) &&
!type.IsSealed &&
!type.IsAbstract &&
(!typeof(IContent).IsAssignableFrom(type) || typeof(ContentPartRecord).IsAssignableFrom(type));

View File

@@ -0,0 +1,16 @@
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor.Models;
using Orchard.Environment.ShellBuilders.Models;
namespace Orchard.Environment.ShellBuilders {
/// <summary>
/// Service at the host level to transform the cachable descriptor into the loadable blueprint.
/// </summary>
public interface ICompositionStrategy {
/// <summary>
/// Using information from the IExtensionManager, transforms and populates all of the
/// blueprint model the shell builders will need to correctly initialize a tenant IoC container.
/// </summary>
ShellBlueprint Compose(ShellSettings settings, ShellDescriptor descriptor);
}
}

View File

@@ -119,7 +119,7 @@ namespace Orchard.Environment {
}
private void CompileDirectory(CompilationContext context, string viewDirectory) {
// Prevent processing of the same directories multiple times (sligh performance optimization,
// Prevent processing of the same directories multiple times (slight performance optimization,
// as the build manager second call to compile a view is essentially a "no-op".
if (context.ProcessedDirectories.Contains(viewDirectory))
return;

View File

@@ -151,8 +151,8 @@
<ItemGroup>
<Compile Include="Data\Migration\Schema\AddUniqueConstraintCommand.cs" />
<Compile Include="Data\Migration\Schema\DropUniqueConstraintCommand.cs" />
<Compile Include="Environment\Extensions\Helpers\ExtensionManagerExtensions.cs" />
<Compile Include="Environment\Extensions\Models\LifecycleStatus.cs" />
<Compile Include="Environment\ShellBuilders\ICompositionStrategy.cs" />
<Compile Include="Mvc\Updater.cs" />
<Compile Include="Recipes\Models\ConfigurationContext.cs" />
<Compile Include="Recipes\Models\RecipeBuilderStepConfigurationContext.cs" />