mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Implementing feature priorities
--HG-- branch : dev
This commit is contained in:
@@ -506,9 +506,7 @@ OrchardVersion: 1
|
||||
Name: Alpha
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Features:
|
||||
Alpha:
|
||||
Dependencies: Gamma
|
||||
Dependencies: Gamma
|
||||
");
|
||||
|
||||
moduleExtensionFolder.Manifests.Add("Beta", @"
|
||||
@@ -520,23 +518,139 @@ OrchardVersion: 1
|
||||
Name: Gamma
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Features:
|
||||
Gamma:
|
||||
Dependencies: Beta
|
||||
Dependencies: Beta
|
||||
");
|
||||
|
||||
moduleExtensionFolder.Manifests.Add("Classic", @"
|
||||
Name: Classic
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Features:
|
||||
Classic:
|
||||
Dependencies: Alpha
|
||||
Dependencies: Alpha
|
||||
");
|
||||
|
||||
IExtensionManager extensionManager = new ExtensionManager(new[] { moduleExtensionFolder, themeExtensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
var features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Gamma<Alpha<Classic<"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FeatureDescriptorsAreInDependencyAndPriorityOrder() {
|
||||
var extensionLoader = new StubLoaders();
|
||||
var extensionFolder = new StubFolders();
|
||||
|
||||
// Check that priorities apply correctly on items on the same level of dependencies and are overwritten by dependencies
|
||||
extensionFolder.Manifests.Add("Alpha", @"
|
||||
Name: Alpha
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Dependencies: Gamma
|
||||
Priority:2"); // More important than Gamma but will get overwritten by the dependency
|
||||
|
||||
extensionFolder.Manifests.Add("Beta", @"
|
||||
Name: Beta
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:2");
|
||||
|
||||
extensionFolder.Manifests.Add("Foo", @"
|
||||
Name: Foo
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:1");
|
||||
|
||||
extensionFolder.Manifests.Add("Gamma", @"
|
||||
Name: Gamma
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Dependencies: Beta, Foo
|
||||
Priority:3");
|
||||
|
||||
IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
var features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Foo<Beta<Gamma<Alpha<"));
|
||||
|
||||
// Change priorities and see that it reflects properly
|
||||
extensionFolder.Manifests["Foo"] = @"
|
||||
Name: Foo
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:3";
|
||||
|
||||
extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Foo<Gamma<Alpha<"));
|
||||
|
||||
// Remove dependency on Beta and see that it moves down the list since no one depends on it anymore
|
||||
extensionFolder.Manifests["Gamma"] = @"
|
||||
Name: Gamma
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Dependencies: Beta
|
||||
Priority:3";
|
||||
|
||||
extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Foo<Gamma<Alpha<"));
|
||||
|
||||
// Change Foo to depend on Gamma and see that it moves to a new position (same dependencies as alpha but lower priority)
|
||||
extensionFolder.Manifests["Foo"] = @"
|
||||
Name: Foo
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:3
|
||||
Dependencies: Gamma";
|
||||
|
||||
extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Gamma<Alpha<Foo<"));
|
||||
|
||||
// Update Foo to a higher priority than alpha
|
||||
extensionFolder.Manifests["Foo"] = @"
|
||||
Name: Foo
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:1
|
||||
Dependencies: Gamma";
|
||||
|
||||
extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Beta<Gamma<Foo<Alpha<"));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void FeatureDescriptorsAreInPriorityOrder() {
|
||||
var extensionLoader = new StubLoaders();
|
||||
var extensionFolder = new StubFolders();
|
||||
|
||||
// Check that priorities apply correctly on items on the same level of dependencies and are overwritten by dependencies
|
||||
extensionFolder.Manifests.Add("Alpha", @"
|
||||
Name: Alpha
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:4"); // More important than Gamma but will get overwritten by the dependency
|
||||
|
||||
extensionFolder.Manifests.Add("Beta", @"
|
||||
Name: Beta
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:3");
|
||||
|
||||
extensionFolder.Manifests.Add("Foo", @"
|
||||
Name: Foo
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:1");
|
||||
|
||||
extensionFolder.Manifests.Add("Gamma", @"
|
||||
Name: Gamma
|
||||
Version: 1.0.3
|
||||
OrchardVersion: 1
|
||||
Priority:2");
|
||||
|
||||
IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }, new StubCacheManager());
|
||||
var features = extensionManager.AvailableFeatures();
|
||||
Assert.That(features.Aggregate("<", (a, b) => a + b.Id + "<"), Is.EqualTo("<Foo<Gamma<Beta<Alpha<"));
|
||||
}
|
||||
}
|
||||
}
|
@@ -37,7 +37,7 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
|
||||
var alterations = builderFactory.BuildAlterations()
|
||||
.Where(alteration => IsModuleOrRequestedTheme(alteration, themeName))
|
||||
.OrderByDependencies(AlterationHasDependency);
|
||||
.OrderByDependenciesAndPriorities(AlterationHasDependency, GetPriority);
|
||||
|
||||
var descriptors = alterations.GroupBy(alteration => alteration.ShapeType, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(group => group.Aggregate(
|
||||
@@ -54,6 +54,10 @@ namespace Orchard.DisplayManagement.Descriptors {
|
||||
});
|
||||
}
|
||||
|
||||
private static int GetPriority(ShapeAlteration shapeAlteration) {
|
||||
return shapeAlteration.Feature.Descriptor.Priority;
|
||||
}
|
||||
|
||||
private static bool AlterationHasDependency(ShapeAlteration item, ShapeAlteration subject) {
|
||||
return ExtensionManager.HasDependency(item.Feature.Descriptor, subject.Feature.Descriptor);
|
||||
}
|
||||
|
@@ -199,9 +199,10 @@ namespace Orchard.Environment.Extensions {
|
||||
.ToDictionary(g => g.Key, g => g.AsEnumerable(), StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var sortedAvailableExtensions =
|
||||
availableExtensions.OrderByDependencies(
|
||||
availableExtensions.OrderByDependenciesAndPriorities(
|
||||
(item, dep) => referencesByModule.ContainsKey(item.Id) &&
|
||||
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)))
|
||||
referencesByModule[item.Id].Any(r => StringComparer.OrdinalIgnoreCase.Equals(dep.Id, r.Name)),
|
||||
(item) => item.Priority)
|
||||
.ToList();
|
||||
|
||||
return new ExtensionLoadingContext {
|
||||
|
@@ -39,7 +39,11 @@ namespace Orchard.Environment.Extensions {
|
||||
|
||||
public IEnumerable<FeatureDescriptor> AvailableFeatures() {
|
||||
return _cacheManager.Get("...", ctx =>
|
||||
AvailableExtensions().SelectMany(ext => ext.Features).OrderByDependencies(HasDependency).ToReadOnlyCollection());
|
||||
AvailableExtensions().SelectMany(ext => ext.Features).OrderByDependenciesAndPriorities(HasDependency, GetPriority).ToReadOnlyCollection());
|
||||
}
|
||||
|
||||
internal static int GetPriority(FeatureDescriptor featureDescriptor) {
|
||||
return featureDescriptor.Priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -100,6 +100,7 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
AntiForgery = GetValue(manifest, "AntiForgery"),
|
||||
Zones = GetValue(manifest, "Zones"),
|
||||
BaseTheme = GetValue(manifest, "BaseTheme"),
|
||||
Priority = int.Parse(GetValue(manifest, "Priority") ?? "0")
|
||||
};
|
||||
extensionDescriptor.Features = GetFeaturesForExtension(manifest, extensionDescriptor);
|
||||
|
||||
@@ -183,6 +184,9 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
case "FeatureDescription":
|
||||
manifest.Add("FeatureDescription", field[1]);
|
||||
break;
|
||||
case "Priority":
|
||||
manifest.Add("Priority", field[1]);
|
||||
break;
|
||||
case "Features":
|
||||
manifest.Add("Features", reader.ReadToEnd());
|
||||
break;
|
||||
@@ -200,6 +204,7 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
FeatureDescriptor defaultFeature = new FeatureDescriptor {
|
||||
Id = extensionDescriptor.Id,
|
||||
Name = extensionDescriptor.Name,
|
||||
Priority = extensionDescriptor.Priority,
|
||||
Description = GetValue(manifest, "FeatureDescription") ?? GetValue(manifest, "Description") ?? string.Empty,
|
||||
Dependencies = ParseFeatureDependenciesEntry(GetValue(manifest, "Dependencies")),
|
||||
Extension = extensionDescriptor,
|
||||
@@ -229,6 +234,7 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
if (featureDescriptorId == extensionDescriptor.Id) {
|
||||
featureDescriptor = defaultFeature;
|
||||
featureDescriptor.Name = extensionDescriptor.Name;
|
||||
featureDescriptor.Priority = extensionDescriptor.Priority;
|
||||
}
|
||||
else {
|
||||
featureDescriptor = new FeatureDescriptor {
|
||||
@@ -256,6 +262,9 @@ namespace Orchard.Environment.Extensions.Folders {
|
||||
case "Category":
|
||||
featureDescriptor.Category = featureField[1];
|
||||
break;
|
||||
case "Priority":
|
||||
featureDescriptor.Priority = int.Parse(featureField[1]);
|
||||
break;
|
||||
case "Dependencies":
|
||||
featureDescriptor.Dependencies = ParseFeatureDependenciesEntry(featureField[1]);
|
||||
break;
|
||||
|
@@ -29,6 +29,7 @@ namespace Orchard.Environment.Extensions.Models {
|
||||
public string AntiForgery { get; set; }
|
||||
public string Zones { get; set; }
|
||||
public string BaseTheme { get; set; }
|
||||
public int Priority { get; set; }
|
||||
|
||||
public IEnumerable<FeatureDescriptor> Features { get; set; }
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ namespace Orchard.Environment.Extensions.Models {
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Category { get; set; }
|
||||
public int Priority { get; set; }
|
||||
public IEnumerable<string> Dependencies { get; set; }
|
||||
}
|
||||
}
|
@@ -22,7 +22,7 @@ namespace Orchard.Environment.ShellBuilders.Models {
|
||||
|
||||
public class ShellBlueprintItem {
|
||||
public Type Type { get; set; }
|
||||
public Feature Feature { get; set; }
|
||||
public Feature Feature { get; set; }
|
||||
}
|
||||
|
||||
public class DependencyBlueprint : ShellBlueprintItem {
|
||||
|
@@ -15,27 +15,82 @@ namespace Orchard.Utility {
|
||||
/// is a dependency of another, this algorithm will return the collection of elements sorted
|
||||
/// so that a given element in the sequence doesn't depend on any other element further in the sequence.
|
||||
/// </summary>
|
||||
public static IEnumerable<T> OrderByDependencies<T>(this IEnumerable<T> elements, Func<T, T, bool> hasDependency) {
|
||||
public static IEnumerable<T> OrderByDependenciesAndPriorities<T>(this IEnumerable<T> elements, Func<T, T, bool> hasDependency, Func<T, int> getPriority) {
|
||||
var population = elements.Select(d => new Linkage<T> {
|
||||
Element = d
|
||||
}).ToArray();
|
||||
}).OrderBy(item => getPriority(item.Element)).ToArray(); // Performing an initial sorting by priorities may optimize performance
|
||||
|
||||
var result = new List<T>();
|
||||
foreach (var item in population) {
|
||||
Add(item, result, population, hasDependency);
|
||||
Add(item, result, population, hasDependency, getPriority);
|
||||
}
|
||||
|
||||
// shift elements forward as possible within priorities and dependencies
|
||||
for (int i = 1; i < result.Count; i++) {
|
||||
int bestPosition = BestPriorityPosition(result, i, hasDependency, getPriority);
|
||||
SwitchAndShift(result, i, bestPosition);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void Add<T>(Linkage<T> item, ICollection<T> list, IEnumerable<Linkage<T>> population, Func<T, T, bool> hasDependency) {
|
||||
private static void Add<T>(Linkage<T> item, ICollection<T> list, IEnumerable<Linkage<T>> population, Func<T, T, bool> hasDependency, Func<T, int> getPriority) {
|
||||
if (item.Used)
|
||||
return;
|
||||
|
||||
item.Used = true;
|
||||
|
||||
foreach (var dependency in population.Where(dep => hasDependency(item.Element, dep.Element))) {
|
||||
Add(dependency, list, population, hasDependency);
|
||||
Add(dependency, list, population, hasDependency, getPriority);
|
||||
}
|
||||
|
||||
list.Add(item.Element);
|
||||
}
|
||||
|
||||
private static int BestPriorityPosition<T>(List<T> list, int index, Func<T, T, bool> hasDependency, Func<T, int> getPriority) {
|
||||
int bestPriority = getPriority(list[index]);
|
||||
int bestIndex = index;
|
||||
|
||||
for (int i = index - 1 ; i >= 0 ; i--) {
|
||||
if (hasDependency(list[index], list[i])) {
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
int currentPriority = getPriority(list[i]);
|
||||
if (currentPriority >= bestPriority) {
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances an element within the list from an initial position to a final position with a lower index.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of each element.</typeparam>
|
||||
/// <param name="list">the list of elements.</param>
|
||||
/// <param name="initialPosition">The initial position within the list.</param>
|
||||
/// <param name="finalPosition">The final position within the list.</param>
|
||||
/// <returns>True if any change was made; false otherwise.</returns>
|
||||
private static bool SwitchAndShift<T>(List<T> list, int initialPosition, int finalPosition) {
|
||||
if (initialPosition < finalPosition) {
|
||||
throw new ArgumentException("finalPosition");
|
||||
}
|
||||
|
||||
if (initialPosition != finalPosition) {
|
||||
T temp = list[initialPosition];
|
||||
|
||||
for (; initialPosition > finalPosition; initialPosition--) {
|
||||
list[initialPosition] = list[initialPosition - 1];
|
||||
}
|
||||
|
||||
list[finalPosition] = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user