mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-14 19:04:51 +08:00
Add ability to configure orchard components via config file
A special file "HostSingletons.config" is read at startup to look for properties to set on host components. --HG-- branch : dev
This commit is contained in:
10
src/Orchard.Web/Config/Sample.HostComponents.config
Normal file
10
src/Orchard.Web/Config/Sample.HostComponents.config
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<HostComponents>
|
||||||
|
<Components>
|
||||||
|
<Component Type="Orchard.Environment.Extensions.Loaders.DynamicExtensionLoader">
|
||||||
|
<Properties>
|
||||||
|
<Property Name="Disabled" Value="true"/>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
|
</Components>
|
||||||
|
</HostComponents>
|
@@ -122,6 +122,9 @@
|
|||||||
<DependentUpon>Global.asax</DependentUpon>
|
<DependentUpon>Global.asax</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Content Include="Config\log4net.config" />
|
<Content Include="Config\log4net.config" />
|
||||||
|
<Content Include="Config\Sample.HostComponents.config">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Content>
|
||||||
<None Include="Media\web.config" />
|
<None Include="Media\web.config" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
137
src/Orchard/Environment/HostComponentsConfigModule.cs
Normal file
137
src/Orchard/Environment/HostComponentsConfigModule.cs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Core;
|
||||||
|
using Module = Autofac.Module;
|
||||||
|
|
||||||
|
namespace Orchard.Environment {
|
||||||
|
/// <summary>
|
||||||
|
/// Alter components instantiations by setting property values defined in a configuration file
|
||||||
|
/// </summary>
|
||||||
|
public class HostComponentsConfigModule : Module {
|
||||||
|
public static class XNames {
|
||||||
|
public const string Xmlns = "";
|
||||||
|
public static readonly XName HostComponents = XName.Get("HostComponents", Xmlns);
|
||||||
|
public static readonly XName Components = XName.Get("Components", Xmlns);
|
||||||
|
public static readonly XName Component = XName.Get("Component", Xmlns);
|
||||||
|
public static readonly XName Properties = XName.Get("Properties", Xmlns);
|
||||||
|
public static readonly XName Property = XName.Get("Property", Xmlns);
|
||||||
|
public static readonly XName Type = XName.Get("Type");
|
||||||
|
public static readonly XName Name = XName.Get("Name");
|
||||||
|
public static readonly XName Value = XName.Get("Value");
|
||||||
|
}
|
||||||
|
|
||||||
|
// component type name => list of [property name, property value]
|
||||||
|
public class PropertyEntry {
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly IDictionary<string, IEnumerable<PropertyEntry>> _config = new Dictionary<string, IEnumerable<PropertyEntry>>();
|
||||||
|
|
||||||
|
public HostComponentsConfigModule() {
|
||||||
|
// Called by the framework, as this class is a "Module"
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostComponentsConfigModule(string fileName) {
|
||||||
|
var doc = XDocument.Load(fileName);
|
||||||
|
foreach (var component in doc.Elements(XNames.HostComponents).Elements(XNames.Components).Elements(XNames.Component)) {
|
||||||
|
var componentType = Attr(component, XNames.Type);
|
||||||
|
if (componentType == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var properties = component
|
||||||
|
.Elements(XNames.Properties)
|
||||||
|
.Elements(XNames.Property)
|
||||||
|
.Select(property => new PropertyEntry { Name = Attr(property, XNames.Name), Value = Attr(property, XNames.Value) })
|
||||||
|
.Where(t => !string.IsNullOrEmpty(t.Name) && !string.IsNullOrEmpty(t.Value))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (!properties.Any())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_config.Add(componentType, properties);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Attr(XElement component, XName name) {
|
||||||
|
var attr = component.Attribute(name);
|
||||||
|
if (attr == null)
|
||||||
|
return null;
|
||||||
|
return attr.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
|
||||||
|
var implementationType = registration.Activator.LimitType;
|
||||||
|
|
||||||
|
IEnumerable<PropertyEntry> properties;
|
||||||
|
if (!_config.TryGetValue(implementationType.FullName, out properties))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// build an array of actions on this type to assign loggers to member properties
|
||||||
|
var injectors = BuildPropertiesInjectors(implementationType, properties).ToArray();
|
||||||
|
|
||||||
|
// if there are no logger properties, there's no reason to hook the activated event
|
||||||
|
if (!injectors.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// otherwise, whan an instance of this component is activated, inject the loggers on the instance
|
||||||
|
registration.Activated += (s, e) => {
|
||||||
|
foreach (var injector in injectors)
|
||||||
|
injector(e.Context, e.Instance);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Action<IComponentContext, object>> BuildPropertiesInjectors(Type componentType, IEnumerable<PropertyEntry> properties) {
|
||||||
|
// Look for settable properties with name in "properties"
|
||||||
|
var settableProperties = componentType
|
||||||
|
.GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Select(p => new {
|
||||||
|
PropertyInfo = p,
|
||||||
|
IndexParameters = p.GetIndexParameters(),
|
||||||
|
Accessors = p.GetAccessors(false),
|
||||||
|
PropertyEntry = properties.Where(t => t.Name == p.Name).FirstOrDefault()
|
||||||
|
})
|
||||||
|
.Where(x => x.PropertyEntry != null) // Must be present in "properties"
|
||||||
|
.Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
|
||||||
|
.Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set
|
||||||
|
|
||||||
|
// Return an array of actions that assign the property value
|
||||||
|
foreach (var entry in settableProperties) {
|
||||||
|
var propertyInfo = entry.PropertyInfo;
|
||||||
|
var propertyEntry = entry.PropertyEntry;
|
||||||
|
|
||||||
|
yield return (ctx, instance) => {
|
||||||
|
object value;
|
||||||
|
if (ChangeToCompatibleType(propertyEntry.Value, propertyInfo.PropertyType, out value))
|
||||||
|
propertyInfo.SetValue(instance, value, null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ChangeToCompatibleType(string value, Type destinationType, out object result) {
|
||||||
|
if (string.IsNullOrEmpty(value)) {
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destinationType.IsAssignableFrom(value.GetType())) {
|
||||||
|
result = value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
result = TypeDescriptor.GetConverter(destinationType).ConvertFrom(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
result = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -108,6 +108,10 @@ namespace Orchard.Environment {
|
|||||||
if (File.Exists(optionalHostConfig))
|
if (File.Exists(optionalHostConfig))
|
||||||
builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReader.DefaultSectionName, optionalHostConfig));
|
builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReader.DefaultSectionName, optionalHostConfig));
|
||||||
|
|
||||||
|
var optionalComponentsConfig = HostingEnvironment.MapPath("~/Config/HostComponents.config");
|
||||||
|
if (File.Exists(optionalComponentsConfig))
|
||||||
|
builder.RegisterModule(new HostComponentsConfigModule(optionalComponentsConfig));
|
||||||
|
|
||||||
|
|
||||||
var container = builder.Build();
|
var container = builder.Build();
|
||||||
|
|
||||||
|
@@ -169,6 +169,7 @@
|
|||||||
<Compile Include="Environment\Extensions\Loaders\RawThemeExtensionLoader.cs" />
|
<Compile Include="Environment\Extensions\Loaders\RawThemeExtensionLoader.cs" />
|
||||||
<Compile Include="Environment\Features\FeatureManager.cs" />
|
<Compile Include="Environment\Features\FeatureManager.cs" />
|
||||||
<Compile Include="Environment\IAssemblyLoader.cs" />
|
<Compile Include="Environment\IAssemblyLoader.cs" />
|
||||||
|
<Compile Include="Environment\HostComponentsConfigModule.cs" />
|
||||||
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
<Compile Include="Environment\ViewsBackgroundCompilation.cs" />
|
||||||
<Compile Include="Environment\WorkContextImplementation.cs" />
|
<Compile Include="Environment\WorkContextImplementation.cs" />
|
||||||
<Compile Include="Environment\WorkContextModule.cs" />
|
<Compile Include="Environment\WorkContextModule.cs" />
|
||||||
|
Reference in New Issue
Block a user