mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Merge
--HG-- branch : dev
This commit is contained in:
@@ -77,7 +77,10 @@ namespace Orchard.Tests.Environment {
|
||||
return Enumerable.Empty<ExtensionEntry>();
|
||||
}
|
||||
|
||||
public ShellTopology_Obsolete GetExtensionsTopology() {
|
||||
public IEnumerable<Type> LoadFeature(string featureName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autofac;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Extensions;
|
||||
using Orchard.Extensions.Loaders;
|
||||
using Orchard.Tests.Extensions.ExtensionTypes;
|
||||
using Yaml.Grammar;
|
||||
|
||||
namespace Orchard.Tests.Extensions {
|
||||
@@ -48,6 +51,20 @@ namespace Orchard.Tests.Extensions {
|
||||
}
|
||||
}
|
||||
|
||||
public class StubLoaders : IExtensionLoader {
|
||||
#region Implementation of IExtensionLoader
|
||||
|
||||
public int Order {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public ExtensionEntry Load(ExtensionDescriptor descriptor) {
|
||||
return new ExtensionEntry { Descriptor = descriptor, ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) } };
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void AvailableExtensionsShouldFollowCatalogLocations() {
|
||||
@@ -190,5 +207,78 @@ features:
|
||||
Assert.That(!type.IsAbstract);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionManagerShouldThrowIfFeatureDoesNotExist() {
|
||||
Assert.Throws<ArgumentException>(() => _manager.LoadFeature("NoSuchFeature"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionManagerTestFeatureAttribute() {
|
||||
var extensionManager = new Moq.Mock<IExtensionManager>();
|
||||
extensionManager.Setup(x => x.ActiveExtensions()).Returns(new[] {
|
||||
new ExtensionEntry {
|
||||
Descriptor = new ExtensionDescriptor {
|
||||
Name = "Module",
|
||||
Features = new[] {
|
||||
new FeatureDescriptor { Name = "Module", ExtensionName = "Module" },
|
||||
new FeatureDescriptor { Name = "TestFeature", ExtensionName = "Module" }
|
||||
}},
|
||||
ExportedTypes = new[] { typeof(Alpha), typeof(Beta), typeof(Phi) }
|
||||
}});
|
||||
|
||||
foreach (var type in extensionManager.Object.ActiveExtensions().SelectMany(x => x.ExportedTypes)) {
|
||||
foreach (OrchardFeatureAttribute featureAttribute in type.GetCustomAttributes(typeof(OrchardFeatureAttribute), false)) {
|
||||
Assert.That(featureAttribute.FeatureName, Is.EqualTo("TestFeature"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionManagerLoadFeatureReturnsTypesFromSpecificFeaturesWithFeatureAttribute() {
|
||||
var extensionLoader = new StubLoaders();
|
||||
var extensionFolder = new StubFolders();
|
||||
|
||||
extensionFolder.Manifests.Add("TestModule", @"
|
||||
name: TestModule
|
||||
version: 1.0.3
|
||||
orchardversion: 1
|
||||
features:
|
||||
TestModule:
|
||||
Description: My test module for Orchard.
|
||||
TestFeature:
|
||||
Description: Contains the Phi type.
|
||||
");
|
||||
|
||||
ExtensionManager extensionManager = new ExtensionManager(new []{extensionFolder}, new [] {extensionLoader});
|
||||
|
||||
foreach (var type in extensionManager.LoadFeature("TestFeature")) {
|
||||
Assert.That(type == typeof(Phi));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionManagerLoadFeatureDoesNotReturnTypesFromNonMatchingFeatures() {
|
||||
var extensionLoader = new StubLoaders();
|
||||
var extensionFolder = new StubFolders();
|
||||
|
||||
extensionFolder.Manifests.Add("TestModule", @"
|
||||
name: TestModule
|
||||
version: 1.0.3
|
||||
orchardversion: 1
|
||||
features:
|
||||
TestModule:
|
||||
Description: My test module for Orchard.
|
||||
TestFeature:
|
||||
Description: Contains the Phi type.
|
||||
");
|
||||
|
||||
ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader });
|
||||
|
||||
foreach (var type in extensionManager.LoadFeature("TestModule")) {
|
||||
Assert.That(type != typeof(Phi));
|
||||
Assert.That((type == typeof(Alpha) || (type == typeof(Beta))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
src/Orchard.Tests/Extensions/ExtensionTypes/StubTypes.cs
Normal file
13
src/Orchard.Tests/Extensions/ExtensionTypes/StubTypes.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Orchard.Extensions;
|
||||
|
||||
namespace Orchard.Tests.Extensions.ExtensionTypes {
|
||||
public class Alpha {
|
||||
}
|
||||
|
||||
public class Beta {
|
||||
}
|
||||
|
||||
[OrchardFeature("TestFeature")]
|
||||
public class Phi {
|
||||
}
|
||||
}
|
@@ -54,7 +54,10 @@ namespace Orchard.Tests.Mvc.Routes {
|
||||
};
|
||||
}
|
||||
|
||||
public ShellTopology_Obsolete GetExtensionsTopology() {
|
||||
public IEnumerable<Type> LoadFeature(string featureName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
@@ -165,6 +165,7 @@
|
||||
<Compile Include="EventsTests.cs" />
|
||||
<Compile Include="Extensions\ExtensionFoldersTests.cs" />
|
||||
<Compile Include="Extensions\ExtensionManagerTests.cs" />
|
||||
<Compile Include="Extensions\ExtensionTypes\StubTypes.cs" />
|
||||
<Compile Include="Localization\NullLocalizerTests.cs" />
|
||||
<Compile Include="Logging\LoggingModuleTests.cs" />
|
||||
<Compile Include="Mvc\ModelBinders\KeyedListModelBinderTests.cs" />
|
||||
|
@@ -33,7 +33,8 @@ namespace Orchard.Extensions {
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
|
||||
// This method does not load extension types, simply parses extension manifests from
|
||||
// the filesystem.
|
||||
public IEnumerable<ExtensionDescriptor> AvailableExtensions() {
|
||||
var availableExtensions = new List<ExtensionDescriptor>();
|
||||
foreach (var folder in _folders) {
|
||||
@@ -44,6 +45,14 @@ namespace Orchard.Extensions {
|
||||
return availableExtensions;
|
||||
}
|
||||
|
||||
// This method loads types from extensions into the ExtensionEntry array.
|
||||
public IEnumerable<ExtensionEntry> ActiveExtensions() {
|
||||
if (_activeExtensions == null) {
|
||||
_activeExtensions = BuildActiveExtensions().ToList();
|
||||
}
|
||||
return _activeExtensions;
|
||||
}
|
||||
|
||||
private static ExtensionDescriptor GetDescriptorForExtension(string name, IExtensionFolders folder) {
|
||||
string extensionType = folder is ThemeFolders ? "Theme" : "Module";
|
||||
var parseResult = folder.ParseManifest(name);
|
||||
@@ -94,17 +103,50 @@ namespace Orchard.Extensions {
|
||||
return featureDescriptors;
|
||||
}
|
||||
|
||||
public IEnumerable<ExtensionEntry> ActiveExtensions() {
|
||||
if (_activeExtensions == null) {
|
||||
_activeExtensions = BuildActiveExtensions().ToList();
|
||||
}
|
||||
return _activeExtensions;
|
||||
}
|
||||
|
||||
public ShellTopology_Obsolete GetExtensionsTopology() {
|
||||
public ShellTopology GetExtensionsTopology() {
|
||||
var types = ActiveExtensions().SelectMany(x => x.ExportedTypes);
|
||||
types = types.Concat(typeof(IOrchardHost).Assembly.GetExportedTypes());
|
||||
return new ShellTopology_Obsolete { Types = types.Where(t => t.IsClass && !t.IsAbstract) };
|
||||
return new ShellTopology { Types = types.Where(t => t.IsClass && !t.IsAbstract) };
|
||||
}
|
||||
|
||||
public IEnumerable<Type> LoadFeature(string featureName) {
|
||||
string extensionName = GetExtensionForFeature(featureName);
|
||||
if (extensionName == null) throw new ArgumentException(T("Feature ") + featureName + T(" was not found in any of the installed extensions"));
|
||||
var extension = ActiveExtensions().Where(x => x.Descriptor.Name == extensionName).FirstOrDefault();
|
||||
if (extension == null) throw new InvalidOperationException(T("Extension ") + extensionName + T(" is not active"));
|
||||
|
||||
var extensionTypes = extension.ExportedTypes.Where(t => t.IsClass && !t.IsAbstract);
|
||||
var featureTypes = new List<Type>();
|
||||
|
||||
foreach (var type in extensionTypes) {
|
||||
string sourceFeature = GetSourceFeatureNameForType(type, extensionName);
|
||||
if (String.Equals(sourceFeature, featureName, StringComparison.OrdinalIgnoreCase)) {
|
||||
featureTypes.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return featureTypes;
|
||||
}
|
||||
|
||||
private static string GetSourceFeatureNameForType(Type type, string extensionName) {
|
||||
foreach (OrchardFeatureAttribute featureAttribute in type.GetCustomAttributes(typeof(OrchardFeatureAttribute), false)) {
|
||||
return featureAttribute.FeatureName;
|
||||
}
|
||||
return extensionName;
|
||||
}
|
||||
|
||||
private string GetExtensionForFeature(string featureName) {
|
||||
foreach (var extensionDescriptor in AvailableExtensions()) {
|
||||
if (String.Equals(extensionDescriptor.Name, featureName, StringComparison.OrdinalIgnoreCase)) {
|
||||
return extensionDescriptor.Name;
|
||||
}
|
||||
foreach (var feature in extensionDescriptor.Features) {
|
||||
if (String.Equals(feature.Name, featureName, StringComparison.OrdinalIgnoreCase)) {
|
||||
return extensionDescriptor.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle) {
|
||||
|
@@ -1,11 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Web;
|
||||
|
||||
namespace Orchard.Extensions {
|
||||
public interface IExtensionManager {
|
||||
IEnumerable<ExtensionDescriptor> AvailableExtensions();
|
||||
IEnumerable<ExtensionEntry> ActiveExtensions();
|
||||
ShellTopology_Obsolete GetExtensionsTopology();
|
||||
IEnumerable<Type> LoadFeature(string featureName);
|
||||
void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle);
|
||||
void UninstallExtension(string extensionType, string extensionName);
|
||||
}
|
||||
|
12
src/Orchard/Extensions/OrchardFeatureAttribute.cs
Normal file
12
src/Orchard/Extensions/OrchardFeatureAttribute.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.Extensions {
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class OrchardFeatureAttribute : Attribute {
|
||||
public OrchardFeatureAttribute(string text) {
|
||||
FeatureName = text;
|
||||
}
|
||||
|
||||
public string FeatureName { get; set; }
|
||||
}
|
||||
}
|
@@ -181,6 +181,7 @@
|
||||
<Compile Include="Extensions\ExtensionFolders.cs" />
|
||||
<Compile Include="Extensions\FeatureDescriptor.cs" />
|
||||
<Compile Include="Extensions\Loaders\AreaExtensionLoader.cs" />
|
||||
<Compile Include="Extensions\OrchardFeatureAttribute.cs" />
|
||||
<Compile Include="Extensions\Records\ExtensionRecord.cs" />
|
||||
<Compile Include="Extensions\ShellTopology.cs" />
|
||||
<Compile Include="Mvc\AntiForgery\ValidateAntiForgeryTokenOrchardAttribute.cs" />
|
||||
|
Reference in New Issue
Block a user