PluginManager TLC from v7

This commit is contained in:
Stephan
2017-03-05 11:51:20 +01:00
parent 3576794a64
commit 824cf30e2e
21 changed files with 737 additions and 685 deletions

View File

@@ -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

View File

@@ -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; }

View File

@@ -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.

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -0,0 +1,5 @@
namespace Umbraco.Core.Plugins
{
public interface IDiscoverable
{ }
}

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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.

View File

@@ -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" />

View File

@@ -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();

View File

@@ -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
{
}

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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
{ }
}

View File

@@ -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>();

View File

@@ -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

View File

@@ -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
{ }
}

View File

@@ -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; }