PluginManager TLC from v7
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom IApplicationStartupHandler that auto subscribes to the applications events
|
||||
/// </summary>
|
||||
public interface IApplicationEventHandler
|
||||
public interface IApplicationEventHandler : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// ApplicationContext is created and other static objects that require initialization have been setup
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
@@ -6,7 +7,7 @@ namespace Umbraco.Core.Cache
|
||||
/// The IcacheRefresher Interface is used for loadbalancing.
|
||||
///
|
||||
/// </summary>
|
||||
public interface ICacheRefresher
|
||||
public interface ICacheRefresher : IDiscoverable
|
||||
{
|
||||
Guid RefresherUniqueId { get; }
|
||||
string Name { get; }
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace Umbraco.Core.Components
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an Umbraco component.
|
||||
/// </summary>
|
||||
public interface IUmbracoComponent
|
||||
public interface IUmbracoComponent : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// Composes the component.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace Umbraco.Core.Persistence.Migrations
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for database migrations
|
||||
/// </summary>
|
||||
public interface IMigration
|
||||
public interface IMigration : IDiscoverable
|
||||
{
|
||||
void Up();
|
||||
void Down();
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
|
||||
if (Completed)
|
||||
_transaction.Complete(); // complete the transaction
|
||||
else
|
||||
_transaction.Dispose(); // abort the transaction
|
||||
_transaction.Dispose(); // abort the transaction - fixme or should we always dispose it?!
|
||||
|
||||
_transaction = null;
|
||||
}
|
||||
|
||||
5
src/Umbraco.Core/Plugins/IDiscoverable.cs
Normal file
5
src/Umbraco.Core/Plugins/IDiscoverable.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Umbraco.Core.Plugins
|
||||
{
|
||||
public interface IDiscoverable
|
||||
{ }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,18 +9,16 @@ using System.Web;
|
||||
using System.Web.Compilation;
|
||||
using Umbraco.Core.DI;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Plugins
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A utility class to find all classes of a certain type by reflection in the current bin folder
|
||||
/// of the web application.
|
||||
/// </summary>
|
||||
public static class TypeFinder
|
||||
{
|
||||
private static volatile HashSet<Assembly> _localFilteredAssemblyCache = null;
|
||||
private static volatile HashSet<Assembly> _localFilteredAssemblyCache;
|
||||
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +55,7 @@ namespace Umbraco.Core.Plugins
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
if ((e.InnerException is SecurityException) == false)
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -93,7 +91,7 @@ namespace Umbraco.Core.Plugins
|
||||
}
|
||||
|
||||
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
|
||||
if (!assemblies.Any())
|
||||
if (assemblies.Any() == false)
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
@@ -105,12 +103,12 @@ namespace Umbraco.Core.Plugins
|
||||
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
|
||||
var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code")));
|
||||
//check if the folder exists and if there are any files in it with the supported file extensions
|
||||
if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())))
|
||||
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))
|
||||
{
|
||||
try
|
||||
{
|
||||
var appCodeAssembly = Assembly.Load("App_Code");
|
||||
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
|
||||
if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already
|
||||
assemblies.Add(appCodeAssembly);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
@@ -122,7 +120,7 @@ namespace Umbraco.Core.Plugins
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
if (e.InnerException is SecurityException == false)
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -139,23 +137,15 @@ namespace Umbraco.Core.Plugins
|
||||
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
|
||||
IEnumerable<Assembly> excludeFromResults = null)
|
||||
{
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
lock (LocalFilteredAssemblyCacheLocker)
|
||||
{
|
||||
lock (LocalFilteredAssemblyCacheLocker)
|
||||
{
|
||||
//double check
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
{
|
||||
_localFilteredAssemblyCache = new HashSet<Assembly>();
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
foreach (var a in assemblies)
|
||||
{
|
||||
_localFilteredAssemblyCache.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_localFilteredAssemblyCache != null)
|
||||
return _localFilteredAssemblyCache;
|
||||
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
|
||||
return _localFilteredAssemblyCache;
|
||||
}
|
||||
return _localFilteredAssemblyCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,9 +164,9 @@ namespace Umbraco.Core.Plugins
|
||||
exclusionFilter = new string[] { };
|
||||
|
||||
return GetAllAssemblies()
|
||||
.Where(x => !excludeFromResults.Contains(x)
|
||||
&& !x.GlobalAssemblyCache
|
||||
&& !exclusionFilter.Any(f => x.FullName.StartsWith(f)));
|
||||
.Where(x => excludeFromResults.Contains(x) == false
|
||||
&& x.GlobalAssemblyCache == false
|
||||
&& exclusionFilter.Any(f => x.FullName.StartsWith(f)) == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -290,9 +280,9 @@ namespace Umbraco.Core.Plugins
|
||||
bool onlyConcreteClasses)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
|
||||
|
||||
return GetClasses(assignTypeFrom, assemblies, onlyConcreteClasses,
|
||||
return GetClassesWithBaseType(assignTypeFrom, assemblies, onlyConcreteClasses,
|
||||
//the additional filter will ensure that any found types also have the attribute applied.
|
||||
t => t.GetCustomAttributes<TAttribute>(false).Any());
|
||||
}
|
||||
@@ -316,9 +306,9 @@ namespace Umbraco.Core.Plugins
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Type> FindClassesOfType<T>(IEnumerable<Assembly> assemblies, bool onlyConcreteClasses)
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
if (assemblies == null) throw new ArgumentNullException(nameof(assemblies));
|
||||
|
||||
return GetClasses(typeof(T), assemblies, onlyConcreteClasses);
|
||||
return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -357,105 +347,9 @@ namespace Umbraco.Core.Plugins
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses)
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<Attribute>(attributeType) == false)
|
||||
throw new ArgumentException("The type specified: " + attributeType + " is not an Attribute type");
|
||||
|
||||
var foundAttributedTypes = new HashSet<Type>();
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the attribute type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain a class that has this attribute.
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(attributeType, assemblyList);
|
||||
|
||||
//get a list of non-referenced assemblies (we'll use this when we recurse below)
|
||||
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
|
||||
|
||||
//loop through the referenced assemblies
|
||||
foreach (var a in referencedAssemblies)
|
||||
{
|
||||
//get all types in this assembly
|
||||
var allTypes = GetTypesWithFormattedException(a)
|
||||
.ToArray();
|
||||
|
||||
var attributedTypes = new Type[] { };
|
||||
try
|
||||
{
|
||||
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
|
||||
//the specified attribute
|
||||
attributedTypes = allTypes
|
||||
.Where(t => (TypeHelper.IsNonStaticClass(t)
|
||||
&& (onlyConcreteClasses == false || t.IsAbstract == false))
|
||||
//the type must have this attribute
|
||||
&& t.GetCustomAttributes(attributeType, false).Any())
|
||||
.ToArray();
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
Current.Logger.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
//add the types to our list to return
|
||||
foreach (var t in attributedTypes)
|
||||
{
|
||||
foundAttributedTypes.Add(t);
|
||||
}
|
||||
|
||||
//get all attributes of the type being searched for
|
||||
var allAttributeTypes = allTypes.Where(attributeType.IsAssignableFrom);
|
||||
|
||||
//now we need to include types that may be inheriting from sub classes of the attribute type being searched for
|
||||
//so we will search in assemblies that reference those types too.
|
||||
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
var subTypeList = subTypesInAssembly
|
||||
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
|
||||
.ToArray();
|
||||
|
||||
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
|
||||
|
||||
//if there's a base class amongst the types then we'll only search for that type.
|
||||
//otherwise we'll have to search for all of them.
|
||||
var subTypesToSearch = new HashSet<Type>();
|
||||
if (baseClassAttempt.Success)
|
||||
{
|
||||
subTypesToSearch.Add(baseClassAttempt.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var t in subTypeList)
|
||||
{
|
||||
subTypesToSearch.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var typeToSearch in subTypesToSearch)
|
||||
{
|
||||
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
|
||||
var foundTypes = FindClassesWithAttribute(typeToSearch, otherAssemblies, onlyConcreteClasses);
|
||||
|
||||
foreach (var f in foundTypes)
|
||||
{
|
||||
foundAttributedTypes.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return foundAttributedTypes;
|
||||
return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the classes with attribute.
|
||||
/// </summary>
|
||||
@@ -479,122 +373,129 @@ namespace Umbraco.Core.Plugins
|
||||
return FindClassesWithAttribute<T>(GetAssembliesWithKnownExclusions());
|
||||
}
|
||||
|
||||
|
||||
#region Private methods
|
||||
|
||||
private static IEnumerable<Type> GetClassesWithAttribute(
|
||||
Type attributeType,
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses)
|
||||
{
|
||||
if (typeof(Attribute).IsAssignableFrom(attributeType) == false)
|
||||
throw new ArgumentException("Type " + attributeType + " is not an Attribute type.");
|
||||
|
||||
var candidateAssemblies = new HashSet<Assembly>(assemblies);
|
||||
var attributeAssemblyIsCandidate = candidateAssemblies.Contains(attributeType.Assembly);
|
||||
candidateAssemblies.Remove(attributeType.Assembly);
|
||||
var types = new List<Type>();
|
||||
|
||||
var stack = new Stack<Assembly>();
|
||||
stack.Push(attributeType.Assembly);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var assembly = stack.Pop();
|
||||
|
||||
Type[] assemblyTypes = null;
|
||||
if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate)
|
||||
{
|
||||
// get all assembly types that can be assigned to baseType
|
||||
try
|
||||
{
|
||||
assemblyTypes = GetTypesWithFormattedException(assembly)
|
||||
.ToArray(); // in try block
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
Current.Logger.Error(typeof(TypeFinder), $"Could not query types on {assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
types.AddRange(assemblyTypes.Where(x =>
|
||||
x.IsClass // only classes
|
||||
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
|
||||
&& x.IsNestedPrivate == false // exclude nested private
|
||||
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
|
||||
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
|
||||
&& x.GetCustomAttributes(attributeType, false).Any())); // marked with the attribute
|
||||
}
|
||||
|
||||
if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false)
|
||||
continue;
|
||||
|
||||
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
|
||||
{
|
||||
candidateAssemblies.Remove(referencing);
|
||||
stack.Push(referencing);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds types that are assignable from the assignTypeFrom parameter and will scan for these types in the assembly
|
||||
/// list passed in, however we will only scan assemblies that have a reference to the assignTypeFrom Type or any type
|
||||
/// deriving from the base type.
|
||||
/// </summary>
|
||||
/// <param name="assignTypeFrom"></param>
|
||||
/// <param name="baseType"></param>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="onlyConcreteClasses"></param>
|
||||
/// <param name="additionalFilter">An additional filter to apply for what types will actually be included in the return value</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<Type> GetClasses(
|
||||
Type assignTypeFrom,
|
||||
private static IEnumerable<Type> GetClassesWithBaseType(
|
||||
Type baseType,
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses,
|
||||
Func<Type, bool> additionalFilter = null)
|
||||
{
|
||||
//the default filter will always return true.
|
||||
if (additionalFilter == null)
|
||||
var candidateAssemblies = new HashSet<Assembly>(assemblies);
|
||||
var baseTypeAssemblyIsCandidate = candidateAssemblies.Contains(baseType.Assembly);
|
||||
candidateAssemblies.Remove(baseType.Assembly);
|
||||
var types = new List<Type>();
|
||||
|
||||
var stack = new Stack<Assembly>();
|
||||
stack.Push(baseType.Assembly);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
additionalFilter = type => true;
|
||||
}
|
||||
var assembly = stack.Pop();
|
||||
|
||||
var foundAssignableTypes = new HashSet<Type>();
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain sub type's of the one we're currently looking for
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(assignTypeFrom, assemblyList);
|
||||
|
||||
//get a list of non-referenced assemblies (we'll use this when we recurse below)
|
||||
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
|
||||
|
||||
//loop through the referenced assemblies
|
||||
foreach (var a in referencedAssemblies)
|
||||
{
|
||||
//get all types in the assembly that are sub types of the current type
|
||||
var allSubTypes = GetTypesWithFormattedException(a)
|
||||
.Where(assignTypeFrom.IsAssignableFrom)
|
||||
.ToArray();
|
||||
|
||||
var filteredTypes = new Type[] { };
|
||||
try
|
||||
// get all assembly types that can be assigned to baseType
|
||||
Type[] assemblyTypes = null;
|
||||
if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate)
|
||||
{
|
||||
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes
|
||||
filteredTypes = allSubTypes
|
||||
.Where(t => (TypeHelper.IsNonStaticClass(t)
|
||||
//Do not include nested private classes - since we are in full trust now this will find those too!
|
||||
&& t.IsNestedPrivate == false
|
||||
&& (onlyConcreteClasses == false || t.IsAbstract == false)
|
||||
//Do not include classes that are flagged to hide from the type finder
|
||||
&& t.GetCustomAttribute<HideFromTypeFinderAttribute>() == null
|
||||
&& additionalFilter(t)))
|
||||
.ToArray();
|
||||
try
|
||||
{
|
||||
assemblyTypes = GetTypesWithFormattedException(assembly)
|
||||
.Where(baseType.IsAssignableFrom)
|
||||
.ToArray(); // in try block
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
Current.Logger.Error(typeof(TypeFinder), $"Could not query types on {assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
types.AddRange(assemblyTypes.Where(x =>
|
||||
x.IsClass // only classes
|
||||
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
|
||||
&& x.IsNestedPrivate == false // exclude nested private
|
||||
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
|
||||
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
|
||||
&& (additionalFilter == null || additionalFilter(x)))); // filter
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
Current.Logger.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
|
||||
|
||||
if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed))
|
||||
continue;
|
||||
}
|
||||
|
||||
//add the types to our list to return
|
||||
foreach (var t in filteredTypes)
|
||||
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
|
||||
{
|
||||
foundAssignableTypes.Add(t);
|
||||
candidateAssemblies.Remove(referencing);
|
||||
stack.Push(referencing);
|
||||
}
|
||||
|
||||
//now we need to include types that may be inheriting from sub classes of the type being searched for
|
||||
//so we will search in assemblies that reference those types too.
|
||||
foreach (var subTypesInAssembly in allSubTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
var subTypeList = subTypesInAssembly
|
||||
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
|
||||
.ToArray();
|
||||
|
||||
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
|
||||
|
||||
//if there's a base class amongst the types then we'll only search for that type.
|
||||
//otherwise we'll have to search for all of them.
|
||||
var subTypesToSearch = new HashSet<Type>();
|
||||
if (baseClassAttempt.Success)
|
||||
{
|
||||
subTypesToSearch.Add(baseClassAttempt.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var t in subTypeList)
|
||||
{
|
||||
subTypesToSearch.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var typeToSearch in subTypesToSearch)
|
||||
{
|
||||
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
|
||||
var foundTypes = GetClasses(typeToSearch, otherAssemblies, onlyConcreteClasses, additionalFilter);
|
||||
|
||||
foreach (var f in foundTypes)
|
||||
{
|
||||
foundAssignableTypes.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return foundAssignableTypes;
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
internal static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
|
||||
@@ -660,7 +561,6 @@ namespace Umbraco.Core.Plugins
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public static Type GetTypeByName(string typeName)
|
||||
{
|
||||
var type = BuildManager.GetType(typeName, false);
|
||||
@@ -678,6 +578,5 @@ namespace Umbraco.Core.Plugins
|
||||
.Select(x => x.GetType(typeName))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,49 +12,60 @@ namespace Umbraco.Core.Plugins
|
||||
/// </summary>
|
||||
internal static class TypeHelper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
|
||||
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
|
||||
= new ConcurrentDictionary<Type, FieldInfo[]>();
|
||||
|
||||
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the method is actually overriding a base method
|
||||
/// </summary>
|
||||
/// <param name="m"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsOverride(MethodInfo m)
|
||||
{
|
||||
return m.GetBaseDefinition().DeclaringType != m.DeclaringType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all assembly references that are referencing the assignTypeFrom Type's assembly found in the assemblyList
|
||||
/// </summary>
|
||||
/// <param name="assignTypeFrom"></param>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="assembly">The referenced assembly.</param>
|
||||
/// <param name="assemblies">A list of assemblies.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot
|
||||
/// reference that assembly, same with the global.asax assembly.
|
||||
/// </remarks>
|
||||
public static Assembly[] GetReferencedAssemblies(Type assignTypeFrom, IEnumerable<Assembly> assemblies)
|
||||
public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
//check if it is the app_code assembly.
|
||||
//check if it is App_global.asax assembly
|
||||
if (assignTypeFrom.Assembly.IsAppCodeAssembly() || assignTypeFrom.Assembly.IsGlobalAsaxAssembly())
|
||||
{
|
||||
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
|
||||
return EmptyAssemblies;
|
||||
}
|
||||
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain sub type's of the one we're currently looking for
|
||||
return assemblies
|
||||
.Where(assembly => assembly == assignTypeFrom.Assembly
|
||||
|| HasReferenceToAssemblyWithName(assembly, assignTypeFrom.Assembly.GetName().Name))
|
||||
.ToArray();
|
||||
|
||||
// find all assembly references that are referencing the current type's assembly since we
|
||||
// should only be scanning those assemblies because any other assembly will definitely not
|
||||
// contain sub type's of the one we're currently looking for
|
||||
var name = assembly.GetName().Name;
|
||||
return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// checks if the assembly has a reference with the same name as the expected assembly name.
|
||||
/// Determines if an assembly references another assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <param name="expectedAssemblyName"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
private static bool HasReferenceToAssemblyWithName(Assembly assembly, string expectedAssemblyName)
|
||||
public static bool HasReference(Assembly assembly, string name)
|
||||
{
|
||||
return assembly
|
||||
.GetReferencedAssemblies()
|
||||
.Select(a => a.Name)
|
||||
.Contains(expectedAssemblyName, StringComparer.Ordinal);
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery - no!
|
||||
foreach (var a in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (string.Equals(a.Name, name, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -152,6 +163,24 @@ namespace Umbraco.Core.Plugins
|
||||
return IsTypeAssignableFrom<TContract>(implementation.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method to determine whether <paramref name="implementation"/> represents a value type.
|
||||
/// </summary>
|
||||
/// <param name="implementation">The implementation.</param>
|
||||
public static bool IsValueType(Type implementation)
|
||||
{
|
||||
return implementation.IsValueType || implementation.IsPrimitive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method to determine whether <paramref name="implementation"/> is an implied value type (<see cref="Type.IsValueType"/>, <see cref="Type.IsEnum"/> or a string).
|
||||
/// </summary>
|
||||
/// <param name="implementation">The implementation.</param>
|
||||
public static bool IsImplicitValueType(Type implementation)
|
||||
{
|
||||
return IsValueType(implementation) || implementation.IsEnum || implementation == typeof (string);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns (and caches) a PropertyInfo from a type
|
||||
/// </summary>
|
||||
@@ -168,8 +197,23 @@ namespace Umbraco.Core.Plugins
|
||||
bool includeIndexed = false,
|
||||
bool caseSensitive = true)
|
||||
{
|
||||
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
|
||||
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
|
||||
.FirstOrDefault(x => caseSensitive ? (x.Name == name) : x.Name.InvariantEquals(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and caches) <see cref="FieldInfo"/> discoverable in the current <see cref="AppDomain"/> for a given <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The source.</param>
|
||||
/// <returns></returns>
|
||||
public static FieldInfo[] CachedDiscoverableFields(Type type)
|
||||
{
|
||||
return GetFieldsCache.GetOrAdd(
|
||||
type,
|
||||
x => type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(y => y.IsInitOnly == false)
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public interface IParameterEditor
|
||||
public interface IParameterEditor : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// The id of the property editor
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
using System;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides published content properties conversion service.
|
||||
/// </summary>
|
||||
public interface IPropertyValueConverter
|
||||
public interface IPropertyValueConverter : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the converter supports a property type.
|
||||
|
||||
@@ -488,6 +488,7 @@
|
||||
<Compile Include="Persistence\SqlContext.cs" />
|
||||
<Compile Include="Persistence\NPocoSqlExtensions.cs" />
|
||||
<Compile Include="Persistence\UnitOfWork\UnitOfWorkBase.cs" />
|
||||
<Compile Include="Plugins\IDiscoverable.cs" />
|
||||
<Compile Include="PropertyEditors\DecimalValidator.cs" />
|
||||
<Compile Include="PropertyEditors\ParameterEditorCollection.cs" />
|
||||
<Compile Include="PropertyEditors\ParameterEditorCollectionBuilder.cs" />
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System.Xml;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Core._Legacy.PackageActions
|
||||
{
|
||||
public interface IPackageAction
|
||||
public interface IPackageAction : IDiscoverable
|
||||
{
|
||||
bool Execute(string packageName, XmlNode xmlData);
|
||||
string Alias();
|
||||
|
||||
@@ -166,11 +166,10 @@ namespace Umbraco.Tests.Plugins
|
||||
</baseType>
|
||||
</plugins>");
|
||||
|
||||
Assert.IsTrue(_manager.DetectLegacyPluginListFile());
|
||||
Assert.IsEmpty(_manager.ReadCache()); // uber-legacy cannot be read
|
||||
|
||||
File.Delete(filePath);
|
||||
|
||||
//now create a valid one
|
||||
File.WriteAllText(filePath, @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<plugins>
|
||||
<baseType type=""umbraco.interfaces.ICacheRefresher"" resolutionType=""FindAllTypes"">
|
||||
@@ -178,19 +177,32 @@ namespace Umbraco.Tests.Plugins
|
||||
</baseType>
|
||||
</plugins>");
|
||||
|
||||
Assert.IsFalse(_manager.DetectLegacyPluginListFile());
|
||||
Assert.IsEmpty(_manager.ReadCache()); // legacy cannot be read
|
||||
|
||||
File.Delete(filePath);
|
||||
|
||||
File.WriteAllText(filePath, @"IContentFinder
|
||||
|
||||
MyContentFinder
|
||||
AnotherContentFinder
|
||||
|
||||
");
|
||||
|
||||
Assert.IsNotNull(_manager.ReadCache()); // works
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Cached_Plugin_File()
|
||||
{
|
||||
var types = new[] { typeof(PluginManager), typeof(PluginManagerTests), typeof(UmbracoContext) };
|
||||
var types = new[] { typeof (PluginManager), typeof (PluginManagerTests), typeof (UmbracoContext) };
|
||||
|
||||
//yes this is silly, none of these types inherit from string, but this is just to test the xml file format
|
||||
_manager.UpdateCachedPluginsFile<string>(types, PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
var typeList1 = new PluginManager.TypeList(typeof (object), null);
|
||||
foreach (var type in types) typeList1.Add(type);
|
||||
_manager.AddTypeList(typeList1);
|
||||
_manager.WriteCache();
|
||||
|
||||
var plugins = _manager.TryGetCachedPluginsFromFile<string>(PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
var diffType = _manager.TryGetCachedPluginsFromFile<string>(PluginManager.TypeResolutionKind.FindAttributedTypes);
|
||||
var plugins = _manager.TryGetCached(typeof (object), null);
|
||||
var diffType = _manager.TryGetCached(typeof (object), typeof (ObsoleteAttribute));
|
||||
|
||||
Assert.IsTrue(plugins.Success);
|
||||
//this will be false since there is no cache of that type resolution kind
|
||||
@@ -206,7 +218,7 @@ namespace Umbraco.Tests.Plugins
|
||||
public void PluginHash_From_String()
|
||||
{
|
||||
var s = "hello my name is someone".GetHashCode().ToString("x", CultureInfo.InvariantCulture);
|
||||
var output = PluginManager.ConvertPluginsHashFromHex(s);
|
||||
var output = PluginManager.ConvertHashToInt64(s);
|
||||
Assert.AreNotEqual(0, output);
|
||||
}
|
||||
|
||||
@@ -255,9 +267,7 @@ namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
var foundTypes1 = _manager.ResolveFindMeTypes();
|
||||
var foundTypes2 = _manager.ResolveFindMeTypes();
|
||||
Assert.AreEqual(1,
|
||||
_manager.GetTypeLists()
|
||||
.Count(x => x.IsTypeList<IFindMe>(PluginManager.TypeResolutionKind.FindAllTypes)));
|
||||
Assert.AreEqual(1, _manager.TypeLists.Count(x => x.BaseType == typeof(IFindMe) && x.AttributeType == null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -304,16 +314,16 @@ namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
var types = new HashSet<PluginManager.TypeList>();
|
||||
|
||||
var propEditors = new PluginManager.TypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
propEditors.AddType(typeof(LabelPropertyEditor));
|
||||
var propEditors = new PluginManager.TypeList(typeof (PropertyEditor), null);
|
||||
propEditors.Add(typeof(LabelPropertyEditor));
|
||||
types.Add(propEditors);
|
||||
|
||||
var found = types.SingleOrDefault(x => x.IsTypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes));
|
||||
var found = types.SingleOrDefault(x => x.BaseType == typeof (PropertyEditor) && x.AttributeType == null);
|
||||
|
||||
Assert.IsNotNull(found);
|
||||
|
||||
//This should not find a type list of this type
|
||||
var shouldNotFind = types.SingleOrDefault(x => x.IsTypeList<IParameterEditor>(PluginManager.TypeResolutionKind.FindAllTypes));
|
||||
var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof (IParameterEditor) && x.AttributeType == null);
|
||||
|
||||
Assert.IsNull(shouldNotFind);
|
||||
}
|
||||
@@ -324,7 +334,7 @@ namespace Umbraco.Tests.Plugins
|
||||
|
||||
}
|
||||
|
||||
public interface IFindMe
|
||||
public interface IFindMe : IDiscoverable
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
internal interface IEditorValidator
|
||||
internal interface IEditorValidator : IDiscoverable
|
||||
{
|
||||
Type ModelType { get; }
|
||||
IEnumerable<ValidationResult> Validate(object model);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Web.HealthCheck
|
||||
{
|
||||
@@ -9,7 +10,7 @@ namespace Umbraco.Web.HealthCheck
|
||||
/// Provides a base class for health checks.
|
||||
/// </summary>
|
||||
[DataContract(Name = "healtCheck", Namespace = "")]
|
||||
public abstract class HealthCheck
|
||||
public abstract class HealthCheck : IDiscoverable
|
||||
{
|
||||
protected HealthCheck()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace Umbraco.Web.Models.Trees
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Web.Models.Trees
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for created applications in the umbraco backoffice
|
||||
/// </summary>
|
||||
public interface IApplication
|
||||
public interface IApplication : IDiscoverable
|
||||
{ }
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Plugins;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.WebApi;
|
||||
@@ -15,7 +16,7 @@ namespace Umbraco.Web.Mvc
|
||||
/// <summary>
|
||||
/// Provides a base class for plugin controllers.
|
||||
/// </summary>
|
||||
public abstract class PluginController : Controller
|
||||
public abstract class PluginController : Controller, IDiscoverable
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, PluginControllerMetadata> MetadataStorage
|
||||
= new ConcurrentDictionary<Type, PluginControllerMetadata>();
|
||||
|
||||
@@ -295,7 +295,7 @@ namespace Umbraco.Web.Services
|
||||
// Load all Applications by attribute and add them to the XML config
|
||||
|
||||
//don't cache the result of this because it is only used once during app startup, caching will just add a bit more mem overhead for no reason
|
||||
var types = Current.PluginManager.ResolveTypesWithAttribute<IApplication, ApplicationAttribute>(cacheResult: false); // fixme - inject
|
||||
var types = Current.PluginManager.ResolveTypesWithAttribute<IApplication, ApplicationAttribute>(cache: false); // fixme - inject
|
||||
|
||||
//since applications don't populate their metadata from the attribute and because it is an interface,
|
||||
//we need to interrogate the attributes for the data. Would be better to have a base class that contains
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace Umbraco.Web.WebApi
|
||||
using Umbraco.Core.Plugins;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for auto-routed Umbraco API controllers.
|
||||
/// </summary>
|
||||
public abstract class UmbracoApiController : UmbracoApiControllerBase
|
||||
public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using LightInject;
|
||||
using Umbraco.Core.DI;
|
||||
using Umbraco.Core.Plugins;
|
||||
using Umbraco.Web.UI.Pages;
|
||||
|
||||
namespace Umbraco.Web._Legacy.Actions
|
||||
@@ -88,7 +89,7 @@ namespace Umbraco.Web._Legacy.Actions
|
||||
}
|
||||
}
|
||||
|
||||
public interface IAction
|
||||
public interface IAction : IDiscoverable
|
||||
{
|
||||
char Letter { get; }
|
||||
bool ShowInNotifier { get; }
|
||||
|
||||
Reference in New Issue
Block a user