Working on feature activation lifecycle

Rationalizing some record names
Adding a shell feature concept, with Install and Enable states of rising/up/falling/down
Host has capability of firing a named event with an explicit configuration - needed for controlled execution of rising/falling state changes notifications

--HG--
branch : dev
This commit is contained in:
Louis DeJardin
2010-05-28 13:03:57 -07:00
parent 0fec7c869f
commit 13f44990ca
38 changed files with 811 additions and 129 deletions

View File

@@ -5,6 +5,9 @@
<Sharing>SOLUTION</Sharing>
<CSharp>
<FormatSettings>
<ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER>False</ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER>
<ALIGN_MULTILINE_FOR_STMT>False</ALIGN_MULTILINE_FOR_STMT>
<ALIGN_MULTIPLE_DECLARATION>False</ALIGN_MULTIPLE_DECLARATION>
<ANONYMOUS_METHOD_DECLARATION_BRACES>END_OF_LINE</ANONYMOUS_METHOD_DECLARATION_BRACES>
<CASE_BLOCK_BRACES>END_OF_LINE</CASE_BLOCK_BRACES>
<FORCE_FIXED_BRACES_STYLE>ALWAYS_ADD</FORCE_FIXED_BRACES_STYLE>
@@ -33,6 +36,7 @@
</MODIFIERS_ORDER>
<OTHER_BRACES>END_OF_LINE</OTHER_BRACES>
<TYPE_DECLARATION_BRACES>END_OF_LINE</TYPE_DECLARATION_BRACES>
<WRAP_LINES>False</WRAP_LINES>
</FormatSettings>
<UsingsSettings />
<Naming2>

View File

@@ -44,7 +44,7 @@ namespace Orchard.Specs.Bindings {
var descriptor = descriptorManager.GetShellDescriptor();
descriptorManager.UpdateShellDescriptor(
descriptor.SerialNumber,
descriptor.EnabledFeatures.Concat(new[] { new ShellFeature { Name = name } }),
descriptor.Features.Concat(new[] { new ShellFeature { Name = name } }),
descriptor.Parameters);
}
});

View File

@@ -5,6 +5,7 @@ using Autofac;
using NUnit.Framework;
using Orchard.Core.Settings.Topology;
using Orchard.Core.Settings.Topology.Records;
using Orchard.Environment.State;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.Events;
@@ -34,9 +35,9 @@ namespace Orchard.Tests.Modules.Settings.Topology {
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] {
typeof (TopologyRecord),
typeof (TopologyFeatureRecord),
typeof (TopologyParameterRecord),
typeof (ShellDescriptorRecord),
typeof (ShellFeatureRecord),
typeof (ShellParameterRecord),
};
}
}
@@ -142,5 +143,21 @@ namespace Orchard.Tests.Modules.Settings.Topology {
Assert.That(eventBus.LastMessageName, Is.EqualTo("IShellDescriptorManagerEventHandler.Changed"));
}
[Test]
public void ManagerReturnsStateForFeaturesInDescriptor() {
var descriptorManager = _container.Resolve<IShellDescriptorManager>();
var stateManager = _container.Resolve<IShellStateProvider>();
var state = stateManager.GetShellState();
Assert.That(state.Features.Count(), Is.EqualTo(0));
descriptorManager.UpdateShellDescriptor(
0,
new[]{new ShellFeature{ Name="Foo"}},
Enumerable.Empty<ShellParameter>());
var state2 = stateManager.GetShellState();
Assert.That(state2.Features.Count(), Is.EqualTo(1));
Assert.That(state2.Features, Has.Some.Property("Name").EqualTo("Foo"));
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;
using NUnit.Framework;
using Orchard.Environment;
using Orchard.Mvc.ModelBinders;
@@ -19,30 +20,30 @@ namespace Orchard.Tests.Environment {
return new ModelBinderDescriptor { Type = type, ModelBinder = modelBinder };
}
[Test]
public void ActivatingRuntimeCausesRoutesAndModelBindersToBePublished() {
//[Test]
//public void ActivatingRuntimeCausesRoutesAndModelBindersToBePublished() {
var provider1 = new StubRouteProvider(new[] { Desc("foo1", "foo1"), Desc("foo2", "foo2") });
var provider2 = new StubRouteProvider(new[] { Desc("foo1", "foo1"), Desc("foo2", "foo2") });
var publisher = new StubRoutePublisher();
// var provider1 = new StubRouteProvider(new[] { Desc("foo1", "foo1"), Desc("foo2", "foo2") });
// var provider2 = new StubRouteProvider(new[] { Desc("foo1", "foo1"), Desc("foo2", "foo2") });
// var publisher = new StubRoutePublisher();
var modelBinderProvider1 = new StubModelBinderProvider(new[] { BinderDesc(typeof(object), null), BinderDesc(typeof(string), null) });
var modelBinderProvider2 = new StubModelBinderProvider(new[] { BinderDesc(typeof(int), null), BinderDesc(typeof(long), null) });
var modelBinderPublisher = new StubModelBinderPublisher();
// var modelBinderProvider1 = new StubModelBinderProvider(new[] { BinderDesc(typeof(object), null), BinderDesc(typeof(string), null) });
// var modelBinderProvider2 = new StubModelBinderProvider(new[] { BinderDesc(typeof(int), null), BinderDesc(typeof(long), null) });
// var modelBinderPublisher = new StubModelBinderPublisher();
var runtime = new DefaultOrchardShell(
new[] { provider1, provider2 },
publisher,
new[] { modelBinderProvider1, modelBinderProvider2 },
modelBinderPublisher,
new ViewEngineCollection { new WebFormViewEngine() },
Enumerable.Empty<IOrchardShellEvents>());
// var runtime = new DefaultOrchardShell(
// new[] { provider1, provider2 },
// publisher,
// new[] { modelBinderProvider1, modelBinderProvider2 },
// modelBinderPublisher,
// new ViewEngineCollection { new WebFormViewEngine() },
// new Mock<IOrchardShellEvents>().Object);
runtime.Activate();
// runtime.Activate();
Assert.That(publisher.Routes.Count(), Is.EqualTo(4));
Assert.That(modelBinderPublisher.ModelBinders.Count(), Is.EqualTo(4));
}
// Assert.That(publisher.Routes.Count(), Is.EqualTo(4));
// Assert.That(modelBinderPublisher.ModelBinders.Count(), Is.EqualTo(4));
//}
public class StubRouteProvider : IRouteProvider {
private readonly IEnumerable<RouteDescriptor> _routes;

View File

@@ -77,7 +77,7 @@ namespace Orchard.Tests.Environment.ShellBuilders {
var factory = _container.Resolve<IShellContextFactory>();
var context = factory.CreateSetupContext(new ShellSettings { Name = "Default" });
Assert.That(context.Descriptor.EnabledFeatures, Has.Some.With.Property("Name").EqualTo("Orchard.Setup"));
Assert.That(context.Descriptor.Features, Has.Some.With.Property("Name").EqualTo("Orchard.Setup"));
}
}
}

View File

@@ -0,0 +1,95 @@
using System.Collections.Generic;
using Autofac;
using Moq;
using NUnit.Framework;
using Orchard.Environment.Configuration;
using Orchard.Environment.ShellBuilders;
using Orchard.Environment.State;
using Orchard.Environment.Topology.Models;
using Orchard.Events;
using Orchard.Tests.Utility;
namespace Orchard.Tests.Environment.State
{
[TestFixture]
public class DefaultProcessingEngineTests
{
private IContainer _container;
private ShellContext _shellContext;
[SetUp]
public void Init()
{
var builder = new ContainerBuilder();
builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>();
builder.RegisterAutoMocking();
_container = builder.Build();
_shellContext = new ShellContext
{
Descriptor = new ShellDescriptor(),
Settings = new ShellSettings(),
LifetimeScope = _container.BeginLifetimeScope(),
};
_container.Mock<IShellContextFactory>()
.Setup(x => x.CreateDescribedContext(_shellContext.Settings, _shellContext.Descriptor))
.Returns(_shellContext);
}
[Test]
public void NoTasksPendingByDefault()
{
var engine = _container.Resolve<IProcessingEngine>();
var pending = engine.AreTasksPending();
Assert.That(pending, Is.False);
}
[Test]
public void ExecuteTaskIsSafeToCallWhenItDoesNothing()
{
var engine = _container.Resolve<IProcessingEngine>();
var pending1 = engine.AreTasksPending();
engine.ExecuteNextTask();
var pending2 = engine.AreTasksPending();
Assert.That(pending1, Is.False);
Assert.That(pending2, Is.False);
}
[Test]
public void CallingAddTaskReturnsResultIdentifierAndCausesPendingToBeTrue()
{
var engine = _container.Resolve<IProcessingEngine>();
var pending1 = engine.AreTasksPending();
var resultId = engine.AddTask(null, null, null, null);
var pending2 = engine.AreTasksPending();
Assert.That(pending1, Is.False);
Assert.That(resultId, Is.Not.Null);
Assert.That(resultId, Is.Not.Empty);
Assert.That(pending2, Is.True);
}
[Test]
public void CallingExecuteCausesEventToFireAndPendingFlagToBeCleared()
{
_container.Mock<IEventBus>()
.Setup(x => x.Notify(It.IsAny<string>(), It.IsAny<Dictionary<string,object>>()));
var engine = _container.Resolve<IProcessingEngine>();
var pending1 = engine.AreTasksPending();
engine.AddTask(_shellContext.Settings, _shellContext.Descriptor, "foo", null);
var pending2 = engine.AreTasksPending();
engine.ExecuteNextTask();
var pending3 = engine.AreTasksPending();
Assert.That(pending1, Is.False);
Assert.That(pending2, Is.True);
Assert.That(pending3, Is.False);
_container.Mock<IEventBus>()
.Verify(x => x.Notify("foo", null));
}
}
}

View File

@@ -83,7 +83,7 @@ namespace Orchard.Tests.Environment.Topology {
var descriptor = new ShellDescriptor {
SerialNumber = 6655321,
EnabledFeatures = new[] {
Features = new[] {
new ShellFeature { Name = "f2"},
new ShellFeature { Name = "f4"},
},

View File

@@ -7,14 +7,14 @@ namespace Orchard.Tests.Environment.Utility {
public static ShellDescriptor TopologyDescriptor() {
var descriptor = new ShellDescriptor {
EnabledFeatures = Enumerable.Empty<ShellFeature>(),
Features = Enumerable.Empty<ShellFeature>(),
Parameters = Enumerable.Empty<ShellParameter>(),
};
return descriptor;
}
public static ShellDescriptor WithFeatures(this ShellDescriptor descriptor, params string[] names) {
descriptor.EnabledFeatures = descriptor.EnabledFeatures.Concat(
descriptor.Features = descriptor.Features.Concat(
names.Select(name => new ShellFeature { Name = name }));
return descriptor;

View File

@@ -182,6 +182,7 @@
<Compile Include="Data\StubLocator.cs" />
<Compile Include="Environment\AutofacUtil\AutofacTests.cs" />
<Compile Include="Environment\AutofacUtil\DynamicProxy2\DynamicProxyTests.cs" />
<Compile Include="Environment\State\DefaultProcessingEngineTests.cs" />
<Compile Include="Environment\RunningShellTableTests.cs" />
<Compile Include="Environment\Utility\Build.cs" />
<Compile Include="Environment\Configuration\AppDataFolderTests.cs" />

View File

@@ -131,9 +131,12 @@
<Compile Include="Settings\Drivers\SiteSettingsDriver.cs" />
<Compile Include="Settings\Models\SiteSettingsRecord.cs" />
<Compile Include="Settings\Permissions.cs" />
<Compile Include="Settings\Topology\Records\TopologyFeatureRecord.cs" />
<Compile Include="Settings\Topology\Records\TopologyParameterRecord.cs" />
<Compile Include="Settings\Topology\Records\TopologyRecord.cs" />
<Compile Include="Settings\State\Records\ShellFeatureStateRecord.cs" />
<Compile Include="Settings\State\Records\ShellStateRecord.cs" />
<Compile Include="Settings\State\ShellStateProvider.cs" />
<Compile Include="Settings\Topology\Records\ShellFeatureRecord.cs" />
<Compile Include="Settings\Topology\Records\ShellParameterRecord.cs" />
<Compile Include="Settings\Topology\Records\ShellDescriptorRecord.cs" />
<Compile Include="Settings\Topology\ShellDescriptorManager.cs" />
<Compile Include="Settings\AdminMenu.cs" />
<Compile Include="Settings\Controllers\AdminController.cs" />

View File

@@ -0,0 +1,10 @@
using Orchard.Environment.State.Models;
namespace Orchard.Core.Settings.State.Records {
public class ShellFeatureStateRecord {
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ShellFeatureState.State InstallState { get; set; }
public virtual ShellFeatureState.State EnableState { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using Orchard.Data.Conventions;
namespace Orchard.Core.Settings.State.Records {
public class ShellStateRecord {
public ShellStateRecord() {
Features = new List<ShellFeatureStateRecord>(); }
public virtual int Id { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<ShellFeatureStateRecord> Features { get; set; }
}
}

View File

@@ -0,0 +1,83 @@
using System.Linq;
using Orchard.Core.Settings.State.Records;
using Orchard.Data;
using Orchard.Environment.State;
using Orchard.Environment.State.Models;
using Orchard.Environment.Topology;
using Orchard.Logging;
namespace Orchard.Core.Settings.State {
public class ShellStateProvider : Component, IShellStateProvider {
private readonly IRepository<ShellStateRecord> _shellStateRepository;
private readonly IShellDescriptorManager _shellDescriptorManager;
public ShellStateProvider(
IRepository<ShellStateRecord> shellStateRepository,
IShellDescriptorManager shellDescriptorManager) {
_shellStateRepository = shellStateRepository;
_shellDescriptorManager = shellDescriptorManager;
}
public ShellState GetShellState() {
var stateRecord = _shellStateRepository.Get(x => true) ?? new ShellStateRecord();
var descriptor = _shellDescriptorManager.GetShellDescriptor();
var extraFeatures = descriptor.Features
.Select(r => r.Name)
.Except(stateRecord.Features.Select(r => r.Name));
return new ShellState {
Features = stateRecord.Features
.Select(featureStateRecord => new ShellFeatureState {
Name = featureStateRecord.Name,
EnableState = featureStateRecord.EnableState,
InstallState = featureStateRecord.InstallState
})
.Concat(extraFeatures.Select(name => new ShellFeatureState {
Name = name
}))
.ToArray(),
};
}
private ShellFeatureStateRecord FeatureRecord(string name) {
var stateRecord = _shellStateRepository.Get(x => true) ?? new ShellStateRecord();
var record = stateRecord.Features.SingleOrDefault(x => x.Name == name);
if (record == null) {
record = new ShellFeatureStateRecord { Name = name };
stateRecord.Features.Add(record);
}
if (stateRecord.Id == 0) {
_shellStateRepository.Create(stateRecord);
}
return record;
}
public void UpdateEnabledState(ShellFeatureState featureState, ShellFeatureState.State value) {
Logger.Debug("Feature {0} EnableState changed from {1} to {2}",
featureState.Name, featureState.EnableState, value);
var featureStateRecord = FeatureRecord(featureState.Name);
if (featureStateRecord.EnableState != featureState.EnableState) {
Logger.Warning("Feature {0} prior EnableState was {1} when {2} was expected",
featureState.Name, featureStateRecord.EnableState, featureState.EnableState);
}
featureStateRecord.EnableState = value;
featureState.EnableState = value;
}
public void UpdateInstalledState(ShellFeatureState featureState, ShellFeatureState.State value) {
Logger.Debug("Feature {0} InstallState changed from {1} to {2}",
featureState.Name, featureState.InstallState, value);
var featureStateRecord = FeatureRecord(featureState.Name);
if (featureStateRecord.InstallState != featureState.InstallState) {
Logger.Warning("Feature {0} prior InstallState was {1} when {2} was expected",
featureState.Name, featureStateRecord.InstallState, featureState.InstallState);
}
featureStateRecord.InstallState = value;
featureState.InstallState = value;
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using Orchard.Data.Conventions;
namespace Orchard.Core.Settings.Topology.Records {
public class ShellDescriptorRecord {
public ShellDescriptorRecord() {
Features=new List<ShellFeatureRecord>();
Parameters=new List<ShellParameterRecord>();
}
public virtual int Id { get; set; }
public virtual int SerialNumber { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<ShellFeatureRecord> Features { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<ShellParameterRecord> Parameters { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
namespace Orchard.Core.Settings.Topology.Records {
public class TopologyFeatureRecord {
public class ShellFeatureRecord {
public virtual int Id { get; set; }
public virtual TopologyRecord TopologyRecord { get; set; }
public virtual ShellDescriptorRecord ShellDescriptorRecord { get; set; }
public virtual string Name { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
namespace Orchard.Core.Settings.Topology.Records {
public class TopologyParameterRecord {
public class ShellParameterRecord {
public virtual int Id { get; set; }
public virtual TopologyRecord TopologyRecord { get; set; }
public virtual ShellDescriptorRecord ShellDescriptorRecord { get; set; }
public virtual string Component { get; set; }
public virtual string Name { get; set; }
public virtual string Value { get; set; }

View File

@@ -1,20 +0,0 @@
using System.Collections.Generic;
using Orchard.Data.Conventions;
namespace Orchard.Core.Settings.Topology.Records {
public class TopologyRecord {
public TopologyRecord() {
EnabledFeatures=new List<TopologyFeatureRecord>();
Parameters=new List<TopologyParameterRecord>();
}
public virtual int Id { get; set; }
public virtual int SerialNumber { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<TopologyFeatureRecord> EnabledFeatures { get; set; }
[CascadeAllDeleteOrphan]
public virtual IList<TopologyParameterRecord> Parameters { get; set; }
}
}

View File

@@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Core.Settings.Topology.Records;
using Orchard.Data;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.Events;
using Orchard.Localization;
namespace Orchard.Core.Settings.Topology {
public class ShellDescriptorManager : IShellDescriptorManager {
private readonly IRepository<TopologyRecord> _topologyRecordRepository;
private readonly IRepository<ShellDescriptorRecord> _shellDescriptorRepository;
private readonly IShellDescriptorManagerEventHandler _events;
public ShellDescriptorManager(
IRepository<TopologyRecord> repository,
IRepository<ShellDescriptorRecord> shellDescriptorRepository,
IShellDescriptorManagerEventHandler events) {
_topologyRecordRepository = repository;
_shellDescriptorRepository = shellDescriptorRepository;
_events = events;
T = NullLocalizer.Instance;
}
@@ -24,20 +22,20 @@ namespace Orchard.Core.Settings.Topology {
Localizer T { get; set; }
public ShellDescriptor GetShellDescriptor() {
TopologyRecord topologyRecord = GetTopologyRecord();
if (topologyRecord == null) return null;
return GetShellTopologyDescriptorFromRecord(topologyRecord);
ShellDescriptorRecord shellDescriptorRecord = GetTopologyRecord();
if (shellDescriptorRecord == null) return null;
return GetShellTopologyDescriptorFromRecord(shellDescriptorRecord);
}
private static ShellDescriptor GetShellTopologyDescriptorFromRecord(TopologyRecord topologyRecord) {
ShellDescriptor descriptor = new ShellDescriptor { SerialNumber = topologyRecord.SerialNumber };
private static ShellDescriptor GetShellTopologyDescriptorFromRecord(ShellDescriptorRecord shellDescriptorRecord) {
ShellDescriptor descriptor = new ShellDescriptor { SerialNumber = shellDescriptorRecord.SerialNumber };
var descriptorFeatures = new List<ShellFeature>();
foreach (var topologyFeatureRecord in topologyRecord.EnabledFeatures) {
foreach (var topologyFeatureRecord in shellDescriptorRecord.Features) {
descriptorFeatures.Add(new ShellFeature { Name = topologyFeatureRecord.Name });
}
descriptor.EnabledFeatures = descriptorFeatures;
descriptor.Features = descriptorFeatures;
var descriptorParameters = new List<ShellParameter>();
foreach (var topologyParameterRecord in topologyRecord.Parameters) {
foreach (var topologyParameterRecord in shellDescriptorRecord.Parameters) {
descriptorParameters.Add(
new ShellParameter {
Component = topologyParameterRecord.Component,
@@ -50,42 +48,43 @@ namespace Orchard.Core.Settings.Topology {
return descriptor;
}
private TopologyRecord GetTopologyRecord() {
var records = from record in _topologyRecordRepository.Table select record;
return records.FirstOrDefault();
private ShellDescriptorRecord GetTopologyRecord() {
return _shellDescriptorRepository.Get(x => true);
}
public void UpdateShellDescriptor(int priorSerialNumber, IEnumerable<ShellFeature> enabledFeatures, IEnumerable<ShellParameter> parameters) {
TopologyRecord topologyRecord = GetTopologyRecord();
var serialNumber = topologyRecord == null ? 0 : topologyRecord.SerialNumber;
ShellDescriptorRecord shellDescriptorRecord = GetTopologyRecord();
var serialNumber = shellDescriptorRecord == null ? 0 : shellDescriptorRecord.SerialNumber;
if (priorSerialNumber != serialNumber)
throw new InvalidOperationException(T("Invalid serial number for shell topology").ToString());
if (topologyRecord == null) {
topologyRecord = new TopologyRecord {SerialNumber = 1};
_topologyRecordRepository.Create(topologyRecord);
if (shellDescriptorRecord == null) {
shellDescriptorRecord = new ShellDescriptorRecord { SerialNumber = 1 };
_shellDescriptorRepository.Create(shellDescriptorRecord);
}
else {
topologyRecord.SerialNumber++;
shellDescriptorRecord.SerialNumber++;
}
topologyRecord.EnabledFeatures.Clear();
shellDescriptorRecord.Features.Clear();
foreach (var feature in enabledFeatures) {
topologyRecord.EnabledFeatures.Add(new TopologyFeatureRecord { Name = feature.Name, TopologyRecord = topologyRecord });
shellDescriptorRecord.Features.Add(new ShellFeatureRecord { Name = feature.Name, ShellDescriptorRecord = shellDescriptorRecord });
}
topologyRecord.Parameters.Clear();
shellDescriptorRecord.Parameters.Clear();
foreach (var parameter in parameters) {
topologyRecord.Parameters.Add(new TopologyParameterRecord {
shellDescriptorRecord.Parameters.Add(new ShellParameterRecord {
Component = parameter.Component,
Name = parameter.Name,
Value = parameter.Value,
TopologyRecord = topologyRecord
ShellDescriptorRecord = shellDescriptorRecord
});
}
_events.Changed(GetShellTopologyDescriptorFromRecord(topologyRecord));
_events.Changed(GetShellTopologyDescriptorFromRecord(shellDescriptorRecord));
}
}
}

View File

@@ -53,7 +53,7 @@ namespace Orchard.Modules.Services {
}
public IEnumerable<IModuleFeature> GetAvailableFeatures() {
var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures;
var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().Features;
return GetInstalledModules()
.SelectMany(m => _extensionManager.LoadFeatures(m.Features))
.Select(
@@ -71,7 +71,7 @@ namespace Orchard.Modules.Services {
public void EnableFeatures(IEnumerable<string> features, bool force) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
var enabledFeatures = shellDescriptor.EnabledFeatures.ToList();
var enabledFeatures = shellDescriptor.Features.ToList();
var featuresToEnable =
features.Select(s => EnableFeature(s, GetAvailableFeatures(), force)).
@@ -95,7 +95,7 @@ namespace Orchard.Modules.Services {
public void DisableFeatures(IEnumerable<string> features, bool force) {
var shellDescriptor = _shellDescriptorManager.GetShellDescriptor();
var enabledFeatures = shellDescriptor.EnabledFeatures.ToList();
var enabledFeatures = shellDescriptor.Features.ToList();
var featuresToDisable =
features.Select(s => DisableFeature(s, GetAvailableFeatures(), force)).SelectMany(

View File

@@ -80,6 +80,7 @@
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />
<Content Include="Views\DisplayTemplates\Fields\Sandbox.BingMap.ascx" />
<Content Include="Views\Page\Edit.aspx" />
<Content Include="Views\Page\Create.aspx" />
<Content Include="Views\Page\Show.aspx" />

View File

@@ -86,7 +86,7 @@ namespace Orchard.Setup.Services {
}
var shellDescriptor = new ShellDescriptor {
EnabledFeatures = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
Features = context.EnabledFeatures.Select(name => new ShellFeature { Name = name })
};
var shellToplogy = _compositionStrategy.Compose(shellSettings, shellDescriptor);
@@ -98,7 +98,7 @@ namespace Orchard.Setup.Services {
environment.Resolve<IShellDescriptorManager>().UpdateShellDescriptor(
0,
shellDescriptor.EnabledFeatures,
shellDescriptor.Features,
shellDescriptor.Parameters);
}

View File

@@ -1,11 +1,13 @@
using System;
using System.Linq;
using System.Threading;
using System.Web.Mvc;
using Autofac;
using System.Collections.Generic;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.ShellBuilders;
using Orchard.Environment.State;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
using Orchard.Logging;
@@ -20,6 +22,7 @@ namespace Orchard.Environment {
private readonly IShellSettingsManager _shellSettingsManager;
private readonly IShellContextFactory _shellContextFactory;
private readonly IRunningShellTable _runningShellTable;
private readonly IProcessingEngine _processingEngine;
private IEnumerable<ShellContext> _current;
@@ -27,11 +30,13 @@ namespace Orchard.Environment {
IShellSettingsManager shellSettingsManager,
IShellContextFactory shellContextFactory,
IRunningShellTable runningShellTable,
IProcessingEngine processingEngine,
ControllerBuilder controllerBuilder) {
//_containerProvider = containerProvider;
_shellSettingsManager = shellSettingsManager;
_shellContextFactory = shellContextFactory;
_runningShellTable = runningShellTable;
_processingEngine = processingEngine;
_controllerBuilder = controllerBuilder;
Logger = NullLogger.Instance;
}
@@ -68,7 +73,6 @@ namespace Orchard.Environment {
return new StandaloneEnvironment(shellContext.LifetimeScope);
}
IEnumerable<ShellContext> BuildCurrent() {
lock (this) {
return _current ?? (_current = CreateAndActivate().ToArray());
@@ -94,7 +98,8 @@ namespace Orchard.Environment {
private void ActivateShell(ShellContext context) {
context.Shell.Activate();
_runningShellTable.Add(context.Settings);
HackSimulateExtensionActivation(context.LifetimeScope);
//refactor:lifecycle
//HackSimulateExtensionActivation(context.LifetimeScope);
}
ShellContext CreateSetupContext() {
@@ -117,21 +122,28 @@ namespace Orchard.Environment {
}
protected virtual void EndRequest() {
if (_processingEngine.AreTasksPending()) {
ThreadPool.QueueUserWorkItem(state => {
while (_processingEngine.AreTasksPending()) {
_processingEngine.ExecuteNextTask();
}
});
}
}
//refactor:lifecycle
//private void HackSimulateExtensionActivation(ILifetimeScope shellContainer) {
// var containerProvider = new FiniteContainerProvider(shellContainer);
// try {
// var requestContainer = containerProvider.RequestLifetime;
private void HackSimulateExtensionActivation(ILifetimeScope shellContainer) {
var containerProvider = new FiniteContainerProvider(shellContainer);
try {
var requestContainer = containerProvider.RequestLifetime;
var hackInstallationGenerator = requestContainer.Resolve<IHackInstallationGenerator>();
hackInstallationGenerator.GenerateActivateEvents();
}
finally {
containerProvider.EndRequestLifetime();
}
}
// var hackInstallationGenerator = requestContainer.Resolve<IHackInstallationGenerator>();
// hackInstallationGenerator.GenerateActivateEvents();
// }
// finally {
// containerProvider.EndRequestLifetime();
// }
//}
void IShellSettingsManagerEventHandler.Saved(ShellSettings settings) {

View File

@@ -6,7 +6,6 @@ using Orchard.Environment.Extensions.Models;
using Orchard.Logging;
using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes;
using Orchard.Utility;
namespace Orchard.Environment {
public class DefaultOrchardShell : IOrchardShell {
@@ -15,21 +14,21 @@ namespace Orchard.Environment {
private readonly IEnumerable<IModelBinderProvider> _modelBinderProviders;
private readonly IModelBinderPublisher _modelBinderPublisher;
private readonly ViewEngineCollection _viewEngines;
private readonly IEnumerable<IOrchardShellEvents> _events;
private readonly IOrchardShellEvents _events;
public DefaultOrchardShell(
IOrchardShellEvents events,
IEnumerable<IRouteProvider> routeProviders,
IRoutePublisher routePublisher,
IEnumerable<IModelBinderProvider> modelBinderProviders,
IModelBinderPublisher modelBinderPublisher,
ViewEngineCollection viewEngines,
IEnumerable<IOrchardShellEvents> events) {
ViewEngineCollection viewEngines) {
_events = events;
_routeProviders = routeProviders;
_routePublisher = routePublisher;
_modelBinderProviders = modelBinderProviders;
_modelBinderPublisher = modelBinderPublisher;
_viewEngines = viewEngines;
_events = events;
Logger = NullLogger.Instance;
}
@@ -42,7 +41,11 @@ namespace Orchard.Environment {
AddOrchardLocationsFormats();
_events.Invoke(x => x.Activated(), Logger);
_events.Activated();
}
public void Terminate() {
_events.Terminating();
}
/// <summary>
@@ -89,14 +92,11 @@ namespace Orchard.Environment {
.ToArray();
}
public void Terminate() {
_events.Invoke(x => x.Terminating(), Logger);
}
private static string ModelsLocationFormat(ExtensionDescriptor descriptor) {
return Path.Combine(Path.Combine(descriptor.Location, descriptor.Name), "Views/Shared/{0}.ascx");
}
}
}

View File

@@ -1,4 +1,5 @@
using Orchard.Environment.Configuration;
using Orchard.Environment.Topology.Models;
namespace Orchard.Environment {
public interface IOrchardHost {

View File

@@ -1,6 +1,16 @@
namespace Orchard.Environment {
public interface IOrchardShellEvents : IEvents {
using Orchard.Environment.Extensions.Models;
using Orchard.Events;
namespace Orchard.Environment {
public interface IOrchardShellEvents : IEventHandler {
void Activated();
void Terminating();
}
public interface IFeatureEventHandler : IEventHandler {
void Install(Feature feature);
void Enable(Feature feature);
void Disable(Feature feature);
void Uninstall(Feature feature);
}
}

View File

@@ -12,6 +12,7 @@ using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Folders;
using Orchard.Environment.Extensions.Loaders;
using Orchard.Environment.ShellBuilders;
using Orchard.Environment.State;
using Orchard.Environment.Topology;
using Orchard.Events;
using Orchard.FileSystems.AppData;
@@ -69,6 +70,8 @@ namespace Orchard.Environment {
builder.RegisterType<ShellContainerFactory>().As<IShellContainerFactory>().SingleInstance();
}
builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>().SingleInstance();
}
builder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();

View File

@@ -1,3 +1,4 @@
using System;
using System.Linq;
using Autofac;
using Orchard.Environment.Configuration;
@@ -21,6 +22,13 @@ namespace Orchard.Environment.ShellBuilders {
/// to display setup user interface.
/// </summary>
ShellContext CreateSetupContext(ShellSettings settings);
/// <summary>
/// Builds a shell context given a specific description of features and parameters.
/// Shell's actual current descriptor has no effect. Does not use or update descriptor cache.
/// </summary>
ShellContext CreateDescribedContext(ShellSettings settings, ShellDescriptor shellDescriptor);
}
public class ShellContextFactory : IShellContextFactory {
@@ -79,7 +87,7 @@ namespace Orchard.Environment.ShellBuilders {
private static ShellDescriptor MinimumTopologyDescriptor() {
return new ShellDescriptor {
SerialNumber = -1,
EnabledFeatures = new[] {
Features = new[] {
new ShellFeature {Name = "Orchard.Framework"},
new ShellFeature {Name = "Settings"},
},
@@ -92,7 +100,7 @@ namespace Orchard.Environment.ShellBuilders {
var descriptor = new ShellDescriptor {
SerialNumber = -1,
EnabledFeatures = new[] { new ShellFeature { Name = "Orchard.Setup" } },
Features = new[] { new ShellFeature { Name = "Orchard.Setup" } },
};
var topology = _compositionStrategy.Compose(settings, descriptor);
@@ -106,5 +114,21 @@ namespace Orchard.Environment.ShellBuilders {
Shell = shellScope.Resolve<IOrchardShell>(),
};
}
public ShellContext CreateDescribedContext(ShellSettings settings, ShellDescriptor shellDescriptor) {
Logger.Debug("Creating described context for tenant {0}", settings.Name);
var topology = _compositionStrategy.Compose(settings, shellDescriptor);
var shellScope = _shellContainerFactory.CreateContainer(settings, topology);
return new ShellContext
{
Settings = settings,
Descriptor = shellDescriptor,
Topology = topology,
LifetimeScope = shellScope,
Shell = shellScope.Resolve<IOrchardShell>(),
};
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Environment.Configuration;
using Orchard.Environment.ShellBuilders;
using Orchard.Environment.Topology.Models;
using Orchard.Events;
using Orchard.Logging;
namespace Orchard.Environment.State {
public class DefaultProcessingEngine : Component, IProcessingEngine {
private readonly IShellContextFactory _shellContextFactory;
private readonly IList<Entry> _entries = new List<Entry>();
public DefaultProcessingEngine(
IShellContextFactory shellContextFactory) {
_shellContextFactory = shellContextFactory;
}
public string AddTask(ShellSettings shellSettings, ShellDescriptor shellDescriptor, string eventName, Dictionary<string, object> parameters) {
var entry = new Entry {
ShellSettings = shellSettings,
ShellDescriptor = shellDescriptor,
MessageName = eventName,
EventData = parameters,
TaskId = Guid.NewGuid().ToString("n"),
ProcessId = Guid.NewGuid().ToString("n"),
};
Logger.Information("Adding event {0} to process {1} for shell {2}",
eventName,
entry.ProcessId,
shellSettings.Name);
lock (_entries) {
_entries.Add(entry);
return entry.ProcessId;
}
}
public class Entry {
public string ProcessId { get; set; }
public string TaskId { get; set; }
public ShellSettings ShellSettings { get; set; }
public ShellDescriptor ShellDescriptor { get; set; }
public string MessageName { get; set; }
public Dictionary<string, object> EventData { get; set; }
}
public bool AreTasksPending() {
lock (_entries)
return _entries.Any();
}
public void ExecuteNextTask() {
Entry entry;
lock (_entries) {
if (!_entries.Any())
return;
entry = _entries.First();
_entries.Remove(entry);
}
Execute(entry);
}
private void Execute(Entry entry) {
var shellContext = _shellContextFactory.CreateDescribedContext(entry.ShellSettings, entry.ShellDescriptor);
using (shellContext.LifetimeScope) {
using (var standaloneEnvironment = new StandaloneEnvironment(shellContext.LifetimeScope)) {
var eventBus = standaloneEnvironment.Resolve<IEventBus>();
Logger.Information("Executing event {0} in process {1} for shell {2}",
entry.MessageName,
entry.ProcessId,
entry.ShellSettings.Name);
eventBus.Notify(entry.MessageName, entry.EventData);
}
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System.Collections.Generic;
using Orchard.Environment.Configuration;
using Orchard.Environment.Topology.Models;
namespace Orchard.Environment.State
{
public interface IProcessingEngine
{
/// <summary>
/// Queue an event to fire inside of an explicitly decribed shell context
/// </summary>
string AddTask(
ShellSettings shellSettings,
ShellDescriptor shellDescriptor,
string messageName,
Dictionary<string, object> parameters);
/// <summary>
/// Called by a component responsible for causing tasks to execute. Can be called from
/// anyplace which needs to know if work needs to be performed.
/// </summary>
bool AreTasksPending();
/// <summary>
/// Called by a component responsible for causing tasks to execute. Must only be called
/// at a point where a full context-specific transaction scope may run. (*Not* inside the processing
/// of a request)
/// </summary>
void ExecuteNextTask();
}
}

View File

@@ -0,0 +1,7 @@
using Orchard.Events;
namespace Orchard.Environment.State {
public interface IShellStateManagerEventHandler : IEventHandler {
void ApplyChanges();
}
}

View File

@@ -0,0 +1,9 @@
using Orchard.Environment.State.Models;
namespace Orchard.Environment.State {
public interface IShellStateProvider : IDependency {
ShellState GetShellState();
void UpdateEnabledState(ShellFeatureState featureState, ShellFeatureState.State value);
void UpdateInstalledState(ShellFeatureState featureState, ShellFeatureState.State value);
}
}

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
namespace Orchard.Environment.State.Models {
public class ShellState {
public ShellState() {
Features = new List<ShellFeatureState>();
}
public IEnumerable<ShellFeatureState> Features { get; set; }
}
public class ShellFeatureState {
public string Name { get; set; }
public State InstallState { get; set; }
public State EnableState { get; set; }
public bool IsInstalled { get { return InstallState == State.Up; } }
public bool IsEnabled { get { return EnableState == State.Up; } }
public bool IsDisabled { get { return EnableState == State.Down || EnableState == State.Undefined; } }
public bool IsUninstalled { get { return InstallState == State.Down || InstallState == State.Undefined; } }
public enum State {
Undefined,
Rising,
Up,
Falling,
Down,
}
}
}

View File

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Environment.State.Models;
using Orchard.Environment.Topology;
using Orchard.Environment.Topology.Models;
namespace Orchard.Environment.State {
public class ShellStateManager : IShellStateManagerEventHandler, IShellDescriptorManagerEventHandler {
private readonly ShellSettings _settings;
private readonly IShellStateProvider _stateProvider;
private readonly IExtensionManager _extensionManager;
private readonly IProcessingEngine _processingEngine;
private readonly IFeatureEventHandler _featureEvents;
public ShellStateManager(
ShellSettings settings,
IShellStateProvider stateProvider,
IExtensionManager extensionManager,
IProcessingEngine processingEngine,
IFeatureEventHandler featureEvents) {
_settings = settings;
_stateProvider = stateProvider;
_extensionManager = extensionManager;
_processingEngine = processingEngine;
_featureEvents = featureEvents;
}
void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) {
// deduce and apply state changes involved
var shellState = _stateProvider.GetShellState();
foreach (var feature in descriptor.Features) {
var featureName = feature.Name;
var featureState = shellState.Features.SingleOrDefault(f => f.Name == featureName);
if (featureState == null) {
featureState = new ShellFeatureState {
Name = featureName
};
shellState.Features = shellState.Features.Concat(new[] { featureState });
}
if (!featureState.IsInstalled) {
_stateProvider.UpdateInstalledState(featureState, ShellFeatureState.State.Rising);
}
if (!featureState.IsEnabled) {
_stateProvider.UpdateEnabledState(featureState, ShellFeatureState.State.Rising);
}
}
foreach (var featureState in shellState.Features) {
var featureName = featureState.Name;
if (descriptor.Features.Any(f => f.Name == featureName)) {
continue;
}
if (!featureState.IsDisabled) {
_stateProvider.UpdateEnabledState(featureState, ShellFeatureState.State.Falling);
}
}
FireApplyChangesIfNeeded();
}
private void FireApplyChangesIfNeeded() {
var shellState = _stateProvider.GetShellState();
if (shellState.Features.Any(FeatureIsChanging)) {
var descriptor = new ShellDescriptor {
Features = shellState.Features
.Where(FeatureShouldBeLoadedForStateChangeNotifications)
.Select(x => new ShellFeature {
Name = x.Name
})
.ToArray()
};
_processingEngine.AddTask(
_settings,
descriptor,
"IShellStateManagerEventHandler.ApplyChanges",
new Dictionary<string, object>());
}
}
private static bool FeatureIsChanging(ShellFeatureState shellFeatureState) {
if (shellFeatureState.EnableState == ShellFeatureState.State.Rising ||
shellFeatureState.EnableState == ShellFeatureState.State.Falling) {
return true;
}
if (shellFeatureState.InstallState == ShellFeatureState.State.Rising ||
shellFeatureState.InstallState == ShellFeatureState.State.Falling) {
return true;
}
return false;
}
private static bool FeatureShouldBeLoadedForStateChangeNotifications(ShellFeatureState shellFeatureState) {
return FeatureIsChanging(shellFeatureState) || shellFeatureState.EnableState == ShellFeatureState.State.Up;
}
void IShellStateManagerEventHandler.ApplyChanges() {
var shellState = _stateProvider.GetShellState();
// start with description of all declared features in order - order preserved with all merging
var orderedFeatureDescriptors = AllFeaturesInOrder();
// merge feature state into ordered list
var orderedFeatureDescriptorsAndStates = orderedFeatureDescriptors
.Select(featureDescriptor => new {
FeatureDescriptor = featureDescriptor,
FeatureState = shellState.Features.FirstOrDefault(s => s.Name == featureDescriptor.Name),
})
.Where(entry => entry.FeatureState != null);
// get loaded feature information
var loadedFeatures = _extensionManager.LoadFeatures(orderedFeatureDescriptorsAndStates.Select(entry => entry.FeatureDescriptor)).ToArray();
// merge loaded feature information into ordered list
var loadedEntries = orderedFeatureDescriptorsAndStates.Select(
entry => new {
Feature = loadedFeatures.SingleOrDefault(f => f.Descriptor == entry.FeatureDescriptor)
?? new Feature {
Descriptor = entry.FeatureDescriptor,
ExportedTypes = Enumerable.Empty<Type>()
},
entry.FeatureDescriptor,
entry.FeatureState,
});
// find feature state that is beyond what's currently available from modules
var additionalState = shellState.Features.Except(loadedEntries.Select(entry => entry.FeatureState));
// create additional stub entries for the sake of firing state change events on missing features
var allEntries = loadedEntries.Concat(additionalState.Select(featureState => {
var featureDescriptor = new FeatureDescriptor {
Name = featureState.Name,
Extension = new ExtensionDescriptor {
Name = featureState.Name
}
};
return new {
Feature = new Feature {
Descriptor = featureDescriptor,
ExportedTypes = Enumerable.Empty<Type>(),
},
FeatureDescriptor = featureDescriptor,
FeatureState = featureState
};
}));
// lower enabled states in reverse order
foreach (var entry in allEntries.Where(entry => entry.FeatureState.EnableState == ShellFeatureState.State.Falling)) {
_featureEvents.Disable(entry.Feature);
_stateProvider.UpdateEnabledState(entry.FeatureState, ShellFeatureState.State.Down);
}
// lower installed states in reverse order
foreach (var entry in allEntries.Where(entry => entry.FeatureState.InstallState == ShellFeatureState.State.Falling)) {
_featureEvents.Uninstall(entry.Feature);
_stateProvider.UpdateInstalledState(entry.FeatureState, ShellFeatureState.State.Down);
}
// raise install and enabled states in order
foreach (var entry in allEntries.Where(entry => IsRising(entry.FeatureState))) {
if (entry.FeatureState.InstallState == ShellFeatureState.State.Rising) {
_featureEvents.Install(entry.Feature);
_stateProvider.UpdateInstalledState(entry.FeatureState, ShellFeatureState.State.Up);
}
if (entry.FeatureState.EnableState == ShellFeatureState.State.Rising) {
_featureEvents.Enable(entry.Feature);
_stateProvider.UpdateEnabledState(entry.FeatureState, ShellFeatureState.State.Up);
}
}
// re-fire if any event handlers initiated additional state changes
FireApplyChangesIfNeeded();
}
private IEnumerable<FeatureDescriptor> AllFeaturesInOrder() {
return OrderByDependencies(_extensionManager.AvailableExtensions().SelectMany(ext => ext.Features));
}
static bool IsRising(ShellFeatureState state) {
return state.InstallState == ShellFeatureState.State.Rising ||
state.EnableState == ShellFeatureState.State.Rising;
}
class Linkage {
public FeatureDescriptor Feature {
get;
set;
}
public bool Used {
get;
set;
}
}
private static IEnumerable<FeatureDescriptor> OrderByDependencies(IEnumerable<FeatureDescriptor> descriptors) {
var population = descriptors.Select(d => new Linkage {
Feature = d
}).ToArray();
var result = new List<FeatureDescriptor>();
foreach (var item in population) {
Add(item, result, population);
}
return result;
}
private static void Add(Linkage item, ICollection<FeatureDescriptor> list, IEnumerable<Linkage> population) {
if (item.Used)
return;
item.Used = true;
var dependencies = item.Feature.Dependencies ?? Enumerable.Empty<string>();
foreach (var dependency in dependencies.SelectMany(d => population.Where(p => p.Feature.Name == d))) {
Add(dependency, list, population);
}
list.Add(item.Feature);
}
}
}

View File

@@ -37,12 +37,12 @@ namespace Orchard.Environment.Topology {
var features = _extensionManager.LoadFeatures(enabledFeatures);
if (descriptor.EnabledFeatures.Any(feature => feature.Name == "Orchard.Framework"))
if (descriptor.Features.Any(feature => feature.Name == "Orchard.Framework"))
features = features.Concat(BuiltinFeatures());
var modules = BuildTopology<DependencyTopology>(features, IsModule, BuildModule);
var modules = BuildTopology(features, IsModule, BuildModule);
var dependencies = BuildTopology(features, IsDependency, (t, f) => BuildDependency(t, f, descriptor));
var controllers = BuildTopology<ControllerTopology>(features, IsController, BuildController);
var controllers = BuildTopology(features, IsController, BuildController);
var records = BuildTopology(features, IsRecord, (t, f) => BuildRecord(t, f, settings));
return new ShellTopology {
@@ -53,7 +53,7 @@ namespace Orchard.Environment.Topology {
}
private static bool IsFeatureEnabledInTopology(FeatureDescriptor featureDescriptor, ShellDescriptor descriptor) {
return descriptor.EnabledFeatures.Any(topologyFeature => topologyFeature.Name == featureDescriptor.Name);
return descriptor.Features.Any(topologyFeature => topologyFeature.Name == featureDescriptor.Name);
}
private static IEnumerable<Feature> BuiltinFeatures() {

View File

@@ -23,10 +23,11 @@ namespace Orchard.Environment.Topology {
int priorSerialNumber,
IEnumerable<ShellFeature> enabledFeatures,
IEnumerable<ShellParameter> parameters);
}
public interface IShellDescriptorManagerEventHandler : IEventHandler {
void Changed(ShellDescriptor descriptor);
}
}

View File

@@ -11,12 +11,12 @@ namespace Orchard.Environment.Topology.Models {
/// </summary>
public class ShellDescriptor {
public ShellDescriptor() {
EnabledFeatures = Enumerable.Empty<ShellFeature>();
Features = Enumerable.Empty<ShellFeature>();
Parameters = Enumerable.Empty<ShellParameter>();
}
public int SerialNumber { get; set; }
public IEnumerable<ShellFeature> EnabledFeatures { get; set; }
public IEnumerable<ShellFeature> Features { get; set; }
public IEnumerable<ShellParameter> Parameters { get; set; }
}
@@ -29,4 +29,5 @@ namespace Orchard.Environment.Topology.Models {
public string Name { get; set; }
public string Value { get; set; }
}
}

View File

@@ -1,12 +1,23 @@
namespace Orchard {
using Orchard.Localization;
using Orchard.Logging;
namespace Orchard {
public interface IDependency {
}
public interface ISingletonDependency : IDependency {
}
public interface ITransientDependency : IDependency {
}
public abstract class Component : IDependency {
protected Component() {
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
}
public ILogger Logger { get; set; }
public Localizer T { get; set; }
}
}

View File

@@ -137,6 +137,11 @@
<Compile Include="Environment\DefaultOrchardShell.cs" />
<Compile Include="Environment\IOrchardShell.cs" />
<Compile Include="Environment\OrchardStarter.cs" />
<Compile Include="Environment\State\DefaultProcessingEngine.cs" />
<Compile Include="Environment\State\IProcessingEngine.cs" />
<Compile Include="Environment\State\IShellStateManagerEventHandler.cs" />
<Compile Include="Environment\State\IShellStateProvider.cs" />
<Compile Include="Environment\State\ShellStateManager.cs" />
<Compile Include="IDependency.cs" />
<Compile Include="Mvc\Html\ThemeExtensions.cs" />
<Compile Include="Mvc\Routes\IRoutePublisher.cs" />
@@ -165,6 +170,7 @@
<Compile Include="ContentManagement\IContentManagerSession.cs" />
<Compile Include="ContentManagement\MetaData\Records\ContentTypePartNameRecord.cs" />
<Compile Include="ContentManagement\Utilities\LazyField.cs" />
<Compile Include="Environment\State\Models\ShellState.cs" />
<Compile Include="FileSystems\WebSite\WebSiteFolder.cs" />
<Compile Include="FileSystems\AppData\IAppDataFolder.cs" />
<Compile Include="FileSystems\WebSite\IWebSiteFolder.cs" />
@@ -495,6 +501,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="ContentManagement\MetaData\Models\" />
<Folder Include="Environment\ProcessEngine\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.