diff --git a/src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs b/src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Core/Cache/CacheRefresherCollectionBuilder.cs rename to src/Umbraco.Abstractions/Cache/CacheRefresherCollectionBuilder.cs diff --git a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs similarity index 80% rename from src/Umbraco.Core/Cache/FastDictionaryAppCache.cs rename to src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs index b38f36a7d8..159f9cd7cb 100644 --- a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCache.cs @@ -10,24 +10,33 @@ namespace Umbraco.Core.Cache /// /// Implements a fast on top of a concurrent dictionary. /// - internal class FastDictionaryAppCache : IAppCache + public class FastDictionaryAppCache : IAppCache { + private readonly ITypeFinder _typeFinder; + + public FastDictionaryAppCache(ITypeFinder typeFinder) + { + _typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + } + /// /// Gets the internal items dictionary, for tests only! /// - internal readonly ConcurrentDictionary> Items = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _items = new ConcurrentDictionary>(); + + public int Count => _items.Count; /// public object Get(string cacheKey) { - Items.TryGetValue(cacheKey, out var result); // else null + _items.TryGetValue(cacheKey, out var result); // else null return result == null ? null : SafeLazy.GetSafeLazyValue(result); // return exceptions as null } /// public object Get(string cacheKey, Func getCacheItem) { - var result = Items.GetOrAdd(cacheKey, k => SafeLazy.GetSafeLazy(getCacheItem)); + var result = _items.GetOrAdd(cacheKey, k => SafeLazy.GetSafeLazy(getCacheItem)); var value = result.Value; // will not throw (safe lazy) if (!(value is SafeLazy.ExceptionHolder eh)) @@ -37,7 +46,7 @@ namespace Umbraco.Core.Cache // which would trick with GetSafeLazyValue, we need to remove by ourselves, // in order NOT to cache exceptions - Items.TryRemove(cacheKey, out result); + _items.TryRemove(cacheKey, out result); eh.Exception.Throw(); // throw once! return null; // never reached } @@ -45,7 +54,7 @@ namespace Umbraco.Core.Cache /// public IEnumerable SearchByKey(string keyStartsWith) { - return Items + return _items .Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)) .Select(kvp => SafeLazy.GetSafeLazyValue(kvp.Value)) .Where(x => x != null); @@ -55,7 +64,7 @@ namespace Umbraco.Core.Cache public IEnumerable SearchByRegex(string regex) { var compiled = new Regex(regex, RegexOptions.Compiled); - return Items + return _items .Where(kvp => compiled.IsMatch(kvp.Key)) .Select(kvp => SafeLazy.GetSafeLazyValue(kvp.Value)) .Where(x => x != null); @@ -64,23 +73,23 @@ namespace Umbraco.Core.Cache /// public void Clear() { - Items.Clear(); + _items.Clear(); } /// public void Clear(string key) { - Items.TryRemove(key, out _); + _items.TryRemove(key, out _); } /// public void ClearOfType(string typeName) { - var type = TypeFinder.GetTypeByName(typeName); + var type = _typeFinder.GetTypeByName(typeName); if (type == null) return; var isInterface = type.IsInterface; - foreach (var kvp in Items + foreach (var kvp in _items .Where(x => { // entry.Value is Lazy and not null, its value may be null @@ -92,7 +101,7 @@ namespace Umbraco.Core.Cache // otherwise remove exact types (not inherited types) return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type)); })) - Items.TryRemove(kvp.Key, out _); + _items.TryRemove(kvp.Key, out _); } /// @@ -101,7 +110,7 @@ namespace Umbraco.Core.Cache var typeOfT = typeof(T); var isInterface = typeOfT.IsInterface; - foreach (var kvp in Items + foreach (var kvp in _items .Where(x => { // entry.Value is Lazy and not null, its value may be null @@ -114,7 +123,7 @@ namespace Umbraco.Core.Cache // otherwise remove exact types (not inherited types) return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT)); })) - Items.TryRemove(kvp.Key, out _); + _items.TryRemove(kvp.Key, out _); } /// @@ -123,7 +132,7 @@ namespace Umbraco.Core.Cache var typeOfT = typeof(T); var isInterface = typeOfT.IsInterface; - foreach (var kvp in Items + foreach (var kvp in _items .Where(x => { // entry.Value is Lazy and not null, its value may be null @@ -139,24 +148,24 @@ namespace Umbraco.Core.Cache // run predicate on the 'public key' part only, ie without prefix && predicate(x.Key, (T)value); })) - Items.TryRemove(kvp.Key, out _); + _items.TryRemove(kvp.Key, out _); } /// public void ClearByKey(string keyStartsWith) { - foreach (var ikvp in Items + foreach (var ikvp in _items .Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))) - Items.TryRemove(ikvp.Key, out _); + _items.TryRemove(ikvp.Key, out _); } /// public void ClearByRegex(string regex) { var compiled = new Regex(regex, RegexOptions.Compiled); - foreach (var ikvp in Items + foreach (var ikvp in _items .Where(kvp => compiled.IsMatch(kvp.Key))) - Items.TryRemove(ikvp.Key, out _); + _items.TryRemove(ikvp.Key, out _); } } } diff --git a/src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs similarity index 96% rename from src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs rename to src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs index eb84423f32..57f4e7b9a2 100644 --- a/src/Umbraco.Core/Cache/FastDictionaryAppCacheBase.cs +++ b/src/Umbraco.Abstractions/Cache/FastDictionaryAppCacheBase.cs @@ -10,8 +10,15 @@ namespace Umbraco.Core.Cache /// /// Provides a base class to fast, dictionary-based implementations. /// - internal abstract class FastDictionaryAppCacheBase : IAppCache + public abstract class FastDictionaryAppCacheBase : IAppCache { + private readonly ITypeFinder _typeFinder; + + protected FastDictionaryAppCacheBase(ITypeFinder typeFinder) + { + _typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + } + // prefix cache keys so we know which one are ours protected const string CacheItemPrefix = "umbrtmche"; @@ -116,7 +123,7 @@ namespace Umbraco.Core.Cache /// public virtual void ClearOfType(string typeName) { - var type = TypeFinder.GetTypeByName(typeName); + var type = _typeFinder.GetTypeByName(typeName); if (type == null) return; var isInterface = type.IsInterface; try diff --git a/src/Umbraco.Core/Cache/HttpRequestAppCache.cs b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs similarity index 61% rename from src/Umbraco.Core/Cache/HttpRequestAppCache.cs rename to src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs index 018726538b..41c930ae86 100644 --- a/src/Umbraco.Core/Cache/HttpRequestAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/HttpRequestAppCache.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web; +using Umbraco.Core.Composing; namespace Umbraco.Core.Cache { @@ -15,37 +16,29 @@ namespace Umbraco.Core.Cache /// or no Items...) then this cache acts as a pass-through and does not cache /// anything. /// - internal class HttpRequestAppCache : FastDictionaryAppCacheBase + public class HttpRequestAppCache : FastDictionaryAppCacheBase { - private readonly HttpContextBase _context; - /// /// Initializes a new instance of the class with a context, for unit tests! /// - public HttpRequestAppCache(HttpContextBase context) + public HttpRequestAppCache(Func requestItems, ITypeFinder typeFinder) : base(typeFinder) { - _context = context; + ContextItems = requestItems; } - /// - /// Initializes a new instance of the class. - /// - /// - /// Will use HttpContext.Current. - /// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current - /// - public HttpRequestAppCache() - { } + private Func ContextItems { get; } - private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items; - - private bool HasContextItems => _context?.Items != null || HttpContext.Current != null; + private bool TryGetContextItems(out IDictionary items) + { + items = ContextItems?.Invoke(); + return items != null; + } /// public override object Get(string key, Func factory) { //no place to cache so just return the callback result - if (HasContextItems == false) return factory(); + if (!TryGetContextItems(out var items)) return factory(); key = GetCacheKey(key); @@ -54,7 +47,7 @@ namespace Umbraco.Core.Cache try { EnterWriteLock(); - result = ContextItems[key] as Lazy; // null if key not found + result = items[key] as Lazy; // null if key not found // cannot create value within the lock, so if result.IsValueCreated is false, just // do nothing here - means that if creation throws, a race condition could cause @@ -63,7 +56,7 @@ namespace Umbraco.Core.Cache if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null { result = SafeLazy.GetSafeLazy(factory); - ContextItems[key] = result; + items[key] = result; } } finally @@ -89,22 +82,22 @@ namespace Umbraco.Core.Cache { const string prefix = CacheItemPrefix + "-"; - if (HasContextItems == false) return Enumerable.Empty(); + if (!TryGetContextItems(out var items)) return Enumerable.Empty(); - return ContextItems.Cast() + return items.Cast() .Where(x => x.Key is string s && s.StartsWith(prefix)); } protected override void RemoveEntry(string key) { - if (HasContextItems == false) return; + if (!TryGetContextItems(out var items)) return; - ContextItems.Remove(key); + items.Remove(key); } protected override object GetEntry(string key) { - return HasContextItems ? ContextItems[key] : null; + return !TryGetContextItems(out var items) ? null : items[key]; } #endregion @@ -117,26 +110,27 @@ namespace Umbraco.Core.Cache protected override void EnterWriteLock() { - if (HasContextItems) - { - // note: cannot keep 'entered' as a class variable here, - // since there is one per request - so storing it within - // ContextItems - which is locked, so this should be safe + if (!TryGetContextItems(out var items)) return; - var entered = false; - Monitor.Enter(ContextItems.SyncRoot, ref entered); - ContextItems[ContextItemsLockKey] = entered; - } + // note: cannot keep 'entered' as a class variable here, + // since there is one per request - so storing it within + // ContextItems - which is locked, so this should be safe + + var entered = false; + Monitor.Enter(items.SyncRoot, ref entered); + items[ContextItemsLockKey] = entered; } protected override void ExitReadLock() => ExitWriteLock(); protected override void ExitWriteLock() { - var entered = (bool?) ContextItems[ContextItemsLockKey] ?? false; + if (!TryGetContextItems(out var items)) return; + + var entered = (bool?)items[ContextItemsLockKey] ?? false; if (entered) - Monitor.Exit(ContextItems.SyncRoot); - ContextItems.Remove(ContextItemsLockKey); + Monitor.Exit(items.SyncRoot); + items.Remove(ContextItemsLockKey); } #endregion diff --git a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs b/src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs similarity index 97% rename from src/Umbraco.Core/Cache/ObjectCacheAppCache.cs rename to src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs index ad7f3d6bf7..208390276a 100644 --- a/src/Umbraco.Core/Cache/ObjectCacheAppCache.cs +++ b/src/Umbraco.Abstractions/Cache/ObjectCacheAppCache.cs @@ -13,13 +13,15 @@ namespace Umbraco.Core.Cache /// public class ObjectCacheAppCache : IAppPolicyCache { + private readonly ITypeFinder _typeFinder; private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); /// /// Initializes a new instance of the . /// - public ObjectCacheAppCache() + public ObjectCacheAppCache(ITypeFinder typeFinder) { + _typeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); // the MemoryCache is created with name "in-memory". That name is // used to retrieve configuration options. It does not identify the memory cache, i.e. // each instance of this class has its own, independent, memory cache. @@ -29,7 +31,7 @@ namespace Umbraco.Core.Cache /// /// Gets the internal memory cache, for tests only! /// - internal ObjectCache MemoryCache { get; private set; } + public ObjectCache MemoryCache { get; private set; } /// public object Get(string key) @@ -178,7 +180,7 @@ namespace Umbraco.Core.Cache /// public virtual void ClearOfType(string typeName) { - var type = TypeFinder.GetTypeByName(typeName); + var type = _typeFinder.GetTypeByName(typeName); if (type == null) return; var isInterface = type.IsInterface; try diff --git a/src/Umbraco.Abstractions/Composing/ITypeFinder.cs b/src/Umbraco.Abstractions/Composing/ITypeFinder.cs new file mode 100644 index 0000000000..f302976dd6 --- /dev/null +++ b/src/Umbraco.Abstractions/Composing/ITypeFinder.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + /// + /// Used to find objects by implemented types, names and/or attributes + /// + public interface ITypeFinder + { + Type GetTypeByName(string name); + + /// + /// Return a list of found local Assemblies that Umbraco should scan for type finding + /// + /// + IEnumerable AssembliesToScan { get; } + + /// + /// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute + /// + /// + /// + /// + /// + /// + IEnumerable FindClassesOfTypeWithAttribute( + Type assignTypeFrom, + Type attributeType, + IEnumerable assemblies = null, + bool onlyConcreteClasses = true); + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + /// + IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies = null, bool onlyConcreteClasses = true); + + /// + /// Finds any classes with the attribute. + /// + /// The attribute type + /// The assemblies. + /// if set to true only concrete classes. + /// + IEnumerable FindClassesWithAttribute( + Type attributeType, + IEnumerable assemblies, + bool onlyConcreteClasses); + } +} diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Abstractions/Composing/TypeFinder.cs similarity index 58% rename from src/Umbraco.Core/Composing/TypeFinder.cs rename to src/Umbraco.Abstractions/Composing/TypeFinder.cs index c07f3ae0f4..9d88153b0a 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Abstractions/Composing/TypeFinder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; using System.IO; @@ -6,98 +7,30 @@ using System.Linq; using System.Reflection; using System.Security; using System.Text; -using System.Web; -using System.Web.Compilation; -using System.Web.Hosting; -using Umbraco.Core.Composing; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Exceptions; using Umbraco.Core.IO; +using Umbraco.Core.Logging; namespace Umbraco.Core.Composing { - /// - /// A utility class to find all classes of a certain type by reflection in the current bin folder - /// of the web application. - /// - public static class TypeFinder + /// + public class TypeFinder : ITypeFinder { - private static volatile HashSet _localFilteredAssemblyCache; - private static readonly object LocalFilteredAssemblyCacheLocker = new object(); - private static readonly List NotifiedLoadExceptionAssemblies = new List(); - private static string[] _assembliesAcceptingLoadExceptions; + private readonly ILogger _logger; - private static string[] AssembliesAcceptingLoadExceptions + public TypeFinder(ILogger logger, ITypeFinderConfig typeFinderConfig = null) { - get + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assembliesAcceptingLoadExceptions = typeFinderConfig?.AssembliesAcceptingLoadExceptions.Where(x => !x.IsNullOrWhiteSpace()).ToArray() ?? Array.Empty(); + _allAssemblies = new Lazy>(() => { - if (_assembliesAcceptingLoadExceptions != null) - return _assembliesAcceptingLoadExceptions; - - var s = ConfigurationManager.AppSettings[Constants.AppSettings.AssembliesAcceptingLoadExceptions]; - return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) - ? Array.Empty() - : s.Split(',').Select(x => x.Trim()).ToArray(); - } - } - - private static bool AcceptsLoadExceptions(Assembly a) - { - if (AssembliesAcceptingLoadExceptions.Length == 0) - return false; - if (AssembliesAcceptingLoadExceptions.Length == 1 && AssembliesAcceptingLoadExceptions[0] == "*") - return true; - var name = a.GetName().Name; // simple name of the assembly - return AssembliesAcceptingLoadExceptions.Any(pattern => - { - if (pattern.Length > name.Length) return false; // pattern longer than name - if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical - if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot - return name.StartsWith(pattern); // and name must start with pattern - }); - } - - /// - /// lazily load a reference to all assemblies and only local assemblies. - /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder - /// - /// - /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been - /// loaded in the CLR, not all assemblies. - /// See these threads: - /// http://issues.umbraco.org/issue/U5-198 - /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app - /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl - /// - internal static HashSet GetAllAssemblies() - { - return AllAssemblies.Value; - } - - //Lazy access to the all assemblies list - private static readonly Lazy> AllAssemblies = new Lazy>(() => - { - HashSet assemblies = null; - try - { - var isHosted = Current.IOHelper.IsHosted; - + HashSet assemblies = null; try - { - if (isHosted) - { - assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - } - } - catch (InvalidOperationException e) - { - if (e.InnerException is SecurityException == false) - throw; - } - - if (assemblies == null) { //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = Current.IOHelper.GetRootDirectoryBinFolder(); + var binFolder = GetRootDirectorySafe(); var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); @@ -122,63 +55,101 @@ namespace Umbraco.Core.Composing } } } - } - //if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies. - if (assemblies.Any() == false) - { + //Since we are only loading in the /bin assemblies above, we will also load in anything that's already loaded (which will include gac items) foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { assemblies.Add(a); } } - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(Current.IOHelper.MapPath(Current.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())) + catch (InvalidOperationException e) { - try - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - catch (FileNotFoundException ex) - { - //this will occur if it cannot load the assembly - Current.Logger.Error(typeof(TypeFinder), ex, "Could not load assembly App_Code"); - } + if (e.InnerException is SecurityException == false) + throw; } - } - catch (InvalidOperationException e) + + return assemblies; + }); + } + + //Lazy access to the all assemblies list + private readonly Lazy> _allAssemblies; + private volatile HashSet _localFilteredAssemblyCache; + private readonly object _localFilteredAssemblyCacheLocker = new object(); + private readonly List _notifiedLoadExceptionAssemblies = new List(); + private static readonly ConcurrentDictionary TypeNamesCache= new ConcurrentDictionary(); + private string _rootDir = ""; + private readonly string[] _assembliesAcceptingLoadExceptions; + + // FIXME - this is only an interim change, once the IIOHelper stuff is merged we should use IIOHelper here + private string GetRootDirectorySafe() + { + if (string.IsNullOrEmpty(_rootDir) == false) { - if (e.InnerException is SecurityException == false) - throw; + return _rootDir; } - return assemblies; - }); + var codeBase = Assembly.GetExecutingAssembly().CodeBase; + var uri = new Uri(codeBase); + var path = uri.LocalPath; + var baseDirectory = Path.GetDirectoryName(path); + if (string.IsNullOrEmpty(baseDirectory)) + throw new PanicException("No root directory could be resolved."); + + _rootDir = baseDirectory.Contains("bin") + ? baseDirectory.Substring(0, baseDirectory.LastIndexOf("bin", StringComparison.OrdinalIgnoreCase) - 1) + : baseDirectory; + + return _rootDir; + } + + private bool AcceptsLoadExceptions(Assembly a) + { + if (_assembliesAcceptingLoadExceptions.Length == 0) + return false; + if (_assembliesAcceptingLoadExceptions.Length == 1 && _assembliesAcceptingLoadExceptions[0] == "*") + return true; + var name = a.GetName().Name; // simple name of the assembly + return _assembliesAcceptingLoadExceptions.Any(pattern => + { + if (pattern.Length > name.Length) return false; // pattern longer than name + if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical + if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot + return name.StartsWith(pattern); // and name must start with pattern + }); + } /// - /// Return a list of found local Assemblies excluding the known assemblies we don't want to scan - /// and excluding the ones passed in and excluding the exclusion list filter, the results of this are - /// cached for performance reasons. + /// lazily load a reference to all assemblies and only local assemblies. + /// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder /// - /// - /// - internal static HashSet GetAssembliesWithKnownExclusions( - IEnumerable excludeFromResults = null) + /// + /// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been + /// loaded in the CLR, not all assemblies. + /// See these threads: + /// http://issues.umbraco.org/issue/U5-198 + /// http://stackoverflow.com/questions/3552223/asp-net-appdomain-currentdomain-getassemblies-assemblies-missing-after-app + /// http://stackoverflow.com/questions/2477787/difference-between-appdomain-getassemblies-and-buildmanager-getreferencedassembl + /// + private IEnumerable GetAllAssemblies() { - lock (LocalFilteredAssemblyCacheLocker) - { - if (_localFilteredAssemblyCache != null) - return _localFilteredAssemblyCache; + return _allAssemblies.Value; + } - var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); - _localFilteredAssemblyCache = new HashSet(assemblies); - return _localFilteredAssemblyCache; + /// + public IEnumerable AssembliesToScan + { + get + { + lock (_localFilteredAssemblyCacheLocker) + { + if (_localFilteredAssemblyCache != null) + return _localFilteredAssemblyCache; + + var assemblies = GetFilteredAssemblies(null, KnownAssemblyExclusionFilter); + _localFilteredAssemblyCache = new HashSet(assemblies); + return _localFilteredAssemblyCache; + } } } @@ -188,7 +159,7 @@ namespace Umbraco.Core.Composing /// /// /// - private static IEnumerable GetFilteredAssemblies( + private IEnumerable GetFilteredAssemblies( IEnumerable excludeFromResults = null, string[] exclusionFilter = null) { @@ -210,7 +181,7 @@ namespace Umbraco.Core.Composing /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// - internal static readonly string[] KnownAssemblyExclusionFilter = { + private static readonly string[] KnownAssemblyExclusionFilter = { "Antlr3.", "AutoMapper,", "AutoMapper.", @@ -261,115 +232,40 @@ namespace Umbraco.Core.Composing }; /// - /// Finds any classes derived from the type T that contain the attribute TAttribute + /// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute /// - /// - /// - /// - public static IEnumerable FindClassesOfTypeWithAttribute() - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(GetAssembliesWithKnownExclusions(), true); - } - - /// - /// Finds any classes derived from the type T that contain the attribute TAttribute - /// - /// - /// - /// - /// - public static IEnumerable FindClassesOfTypeWithAttribute(IEnumerable assemblies) - where TAttribute : Attribute - { - return FindClassesOfTypeWithAttribute(assemblies, true); - } - - /// - /// Finds any classes derived from the type T that contain the attribute TAttribute - /// - /// - /// + /// + /// /// /// /// - public static IEnumerable FindClassesOfTypeWithAttribute( - IEnumerable assemblies, - bool onlyConcreteClasses) - where TAttribute : Attribute + public IEnumerable FindClassesOfTypeWithAttribute( + Type assignTypeFrom, + Type attributeType, + IEnumerable assemblies = null, + bool onlyConcreteClasses = true) { - return FindClassesOfTypeWithAttribute(typeof(T), assemblies, onlyConcreteClasses); + var assemblyList = (assemblies ?? AssembliesToScan).ToList(); + + return GetClassesWithBaseType(assignTypeFrom, assemblyList, onlyConcreteClasses, + //the additional filter will ensure that any found types also have the attribute applied. + t => t.GetCustomAttributes(attributeType, false).Any()); } /// - /// Finds any classes derived from the assignTypeFrom Type that contain the attribute TAttribute + /// Returns all types found of in the assemblies specified of type T /// - /// /// /// /// /// - public static IEnumerable FindClassesOfTypeWithAttribute( - Type assignTypeFrom, - IEnumerable assemblies, - bool onlyConcreteClasses) - where TAttribute : Attribute + public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies = null, bool onlyConcreteClasses = true) { - if (assemblies == null) throw new ArgumentNullException(nameof(assemblies)); + var assemblyList = (assemblies ?? AssembliesToScan).ToList(); - return GetClassesWithBaseType(assignTypeFrom, assemblies, onlyConcreteClasses, - //the additional filter will ensure that any found types also have the attribute applied. - t => t.GetCustomAttributes(false).Any()); + return GetClassesWithBaseType(assignTypeFrom, assemblyList, onlyConcreteClasses); } - - /// - /// Searches all filtered local assemblies specified for classes of the type passed in. - /// - /// - /// - public static IEnumerable FindClassesOfType() - { - return FindClassesOfType(GetAssembliesWithKnownExclusions(), true); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies, bool onlyConcreteClasses) - { - if (assemblies == null) throw new ArgumentNullException(nameof(assemblies)); - - return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses); - } - - /// - /// Returns all types found of in the assemblies specified of type T - /// - /// - /// - /// - public static IEnumerable FindClassesOfType(IEnumerable assemblies) - { - return FindClassesOfType(assemblies, true); - } - - /// - /// Finds the classes with attribute. - /// - /// - /// The assemblies. - /// if set to true only concrete classes. - /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies, bool onlyConcreteClasses) - where T : Attribute - { - return FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); - } - + /// /// Finds any classes with the attribute. /// @@ -377,40 +273,58 @@ namespace Umbraco.Core.Composing /// The assemblies. /// if set to true only concrete classes. /// - public static IEnumerable FindClassesWithAttribute( + public IEnumerable FindClassesWithAttribute( Type attributeType, - IEnumerable assemblies, - bool onlyConcreteClasses) + IEnumerable assemblies = null, + bool onlyConcreteClasses = true) { - return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses); + var assemblyList = (assemblies ?? AssembliesToScan).ToList(); + + return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses); } /// - /// Finds the classes with attribute. + /// Returns a Type for the string type name /// - /// - /// The assemblies. + /// /// - public static IEnumerable FindClassesWithAttribute(IEnumerable assemblies) - where T : Attribute + public virtual Type GetTypeByName(string name) { - return FindClassesWithAttribute(assemblies, true); - } + // This is exactly what the BuildManager does, if the type is an assembly qualified type + // name it will find it. + if (TypeNameContainsAssembly(name)) + { + return Type.GetType(name); + } - /// - /// Finds the classes with attribute in filtered local assemblies - /// - /// - /// - public static IEnumerable FindClassesWithAttribute() - where T : Attribute - { - return FindClassesWithAttribute(GetAssembliesWithKnownExclusions()); + // It didn't parse, so try loading from each already loaded assembly and cache it + return TypeNamesCache.GetOrAdd(name, s => + AppDomain.CurrentDomain.GetAssemblies() + .Select(x => x.GetType(s)) + .FirstOrDefault(x => x != null)); } #region Private methods - private static IEnumerable GetClassesWithAttribute( + // borrowed from aspnet System.Web.UI.Util + private static bool TypeNameContainsAssembly(string typeName) + { + return CommaIndexInTypeName(typeName) > 0; + } + + // borrowed from aspnet System.Web.UI.Util + private static int CommaIndexInTypeName(string typeName) + { + var num1 = typeName.LastIndexOf(','); + if (num1 < 0) + return -1; + var num2 = typeName.LastIndexOf(']'); + if (num2 > num1) + return -1; + return typeName.IndexOf(',', num2 + 1); + } + + private IEnumerable GetClassesWithAttribute( Type attributeType, IEnumerable assemblies, bool onlyConcreteClasses) @@ -441,7 +355,7 @@ namespace Umbraco.Core.Composing } catch (TypeLoadException ex) { - Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); + _logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); continue; } @@ -477,7 +391,7 @@ namespace Umbraco.Core.Composing /// /// An additional filter to apply for what types will actually be included in the return value /// - private static IEnumerable GetClassesWithBaseType( + private IEnumerable GetClassesWithBaseType( Type baseType, IEnumerable assemblies, bool onlyConcreteClasses, @@ -507,7 +421,7 @@ namespace Umbraco.Core.Composing } catch (TypeLoadException ex) { - Current.Logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); + _logger.Error(typeof(TypeFinder), ex, "Could not query types on {Assembly} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly); continue; } @@ -533,7 +447,7 @@ namespace Umbraco.Core.Composing return types; } - internal static IEnumerable GetTypesWithFormattedException(Assembly a) + private IEnumerable GetTypesWithFormattedException(Assembly a) { //if the assembly is dynamic, do not try to scan it if (a.IsDynamic) @@ -569,12 +483,12 @@ namespace Umbraco.Core.Composing if (AcceptsLoadExceptions(a) == false) throw ex; // log a warning, and return what we can - lock (NotifiedLoadExceptionAssemblies) + lock (_notifiedLoadExceptionAssemblies) { - if (NotifiedLoadExceptionAssemblies.Contains(a.FullName) == false) + if (_notifiedLoadExceptionAssemblies.Contains(a.FullName) == false) { - NotifiedLoadExceptionAssemblies.Add(a.FullName); - Current.Logger.Warn(typeof (TypeFinder), ex, "Could not load all types from {TypeName}.", a.GetName().Name); + _notifiedLoadExceptionAssemblies.Add(a.FullName); + _logger.Warn(typeof (TypeFinder), ex, "Could not load all types from {TypeName}.", a.GetName().Name); } } return rex.Types.WhereNotNull().ToArray(); @@ -595,8 +509,7 @@ namespace Umbraco.Core.Composing sb.Append(". "); sb.Append(loaderException.GetType().FullName); - var tloadex = loaderException as TypeLoadException; - if (tloadex != null) + if (loaderException is TypeLoadException tloadex) { sb.Append(" on "); sb.Append(tloadex.TypeName); @@ -609,24 +522,6 @@ namespace Umbraco.Core.Composing #endregion - public static Type GetTypeByName(string typeName) - { - var type = BuildManager.GetType(typeName, false); - if (type != null) return type; - // TODO: This isn't very elegant, and will have issues since the AppDomain.CurrentDomain - // doesn't actualy load in all assemblies, only the types that have been referenced so far. - // However, in a web context, the BuildManager will have executed which will force all assemblies - // to be loaded so it's fine for now. - // It could be fairly easy to parse the typeName to get the assembly name and then do Assembly.Load and - // find the type from there. - - //now try fall back procedures. - type = Type.GetType(typeName); - if (type != null) return type; - return AppDomain.CurrentDomain.GetAssemblies() - .Select(x => x.GetType(typeName)) - .FirstOrDefault(x => x != null); - } } } diff --git a/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs b/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs new file mode 100644 index 0000000000..e364790556 --- /dev/null +++ b/src/Umbraco.Abstractions/Composing/TypeFinderExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Umbraco.Core.Composing +{ + public static class TypeFinderExtensions + { + /// + /// Finds any classes derived from the type T that contain the attribute TAttribute + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable FindClassesOfTypeWithAttribute(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + where TAttribute : Attribute + => typeFinder.FindClassesOfTypeWithAttribute(typeof(T), typeof(TAttribute), assemblies, onlyConcreteClasses); + + /// + /// Returns all types found of in the assemblies specified of type T + /// + /// + /// + /// + /// + /// + public static IEnumerable FindClassesOfType(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + => typeFinder.FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses); + + /// + /// Finds the classes with attribute. + /// + /// + /// + /// The assemblies. + /// if set to true only concrete classes. + /// + public static IEnumerable FindClassesWithAttribute(this ITypeFinder typeFinder, IEnumerable assemblies = null, bool onlyConcreteClasses = true) + where T : Attribute + => typeFinder.FindClassesWithAttribute(typeof(T), assemblies, onlyConcreteClasses); + } +} diff --git a/src/Umbraco.Abstractions/Composing/TypeHelper.cs b/src/Umbraco.Abstractions/Composing/TypeHelper.cs index 40a1adc118..28eab6a5ec 100644 --- a/src/Umbraco.Abstractions/Composing/TypeHelper.cs +++ b/src/Umbraco.Abstractions/Composing/TypeHelper.cs @@ -8,6 +8,7 @@ using System.Reflection; namespace Umbraco.Core.Composing { + /// /// A utility class for type checking, this provides internal caching so that calls to these methods will be faster /// than doing a manual type check in c# @@ -18,9 +19,11 @@ namespace Umbraco.Core.Composing = new ConcurrentDictionary, PropertyInfo[]>(); private static readonly ConcurrentDictionary GetFieldsCache = new ConcurrentDictionary(); - + private static readonly Assembly[] EmptyAssemblies = new Assembly[0]; + + /// /// Based on a type we'll check if it is IEnumerable{T} (or similar) and if so we'll return a List{T}, this will also deal with array types and return List{T} for those too. /// If it cannot be done, null is returned. diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Abstractions/Composing/TypeLoader.cs similarity index 91% rename from src/Umbraco.Core/Composing/TypeLoader.cs rename to src/Umbraco.Abstractions/Composing/TypeLoader.cs index d2dffb8ffc..d2f203e112 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Abstractions/Composing/TypeLoader.cs @@ -5,30 +5,30 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading; -using System.Web; -using System.Web.Compilation; using Umbraco.Core.Cache; using Umbraco.Core.Collections; -using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using File = System.IO.File; namespace Umbraco.Core.Composing { + + /// /// Provides methods to find and instantiate types. /// /// - /// This class should be used to get all types, the class should never be used directly. + /// This class should be used to get all types, the class should never be used directly. /// In most cases this class is not used directly but through extension methods that retrieve specific types. /// This class caches the types it knows to avoid excessive assembly scanning and shorten startup times, relying /// on a hash of the DLLs in the ~/bin folder to check for cache expiration. /// - public class TypeLoader + public sealed class TypeLoader { private const string CacheKey = "umbraco-types.list"; + private readonly IIOHelper _ioHelper; private readonly IAppPolicyCache _runtimeCache; private readonly IProfilingLogger _logger; @@ -42,31 +42,40 @@ namespace Umbraco.Core.Composing private string _currentAssembliesHash; private IEnumerable _assemblies; private bool _reportedChange; - private readonly string _localTempPath; + private readonly DirectoryInfo _localTempPath; private string _fileBasePath; /// /// Initializes a new instance of the class. /// + /// + /// /// The application runtime cache. /// Files storage location. /// A profiling logger. - public TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger) - : this(runtimeCache, localTempPath, logger, true) + /// + public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, IEnumerable assembliesToScan = null) + : this(ioHelper, typeFinder, runtimeCache, localTempPath, logger, true, assembliesToScan) { } /// /// Initializes a new instance of the class. /// + /// + /// /// The application runtime cache. /// Files storage location. /// A profiling logger. /// Whether to detect changes using hashes. - internal TypeLoader(IAppPolicyCache runtimeCache, string localTempPath, IProfilingLogger logger, bool detectChanges) + /// + public TypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, IProfilingLogger logger, bool detectChanges, IEnumerable assembliesToScan = null) { + TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + _ioHelper = ioHelper; _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); _localTempPath = localTempPath; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _assemblies = assembliesToScan; if (detectChanges) { @@ -99,11 +108,10 @@ namespace Umbraco.Core.Composing } /// - /// Initializes a new, test/blank, instance of the class. + /// Returns the underlying /// - /// The initialized instance cannot get types. - internal TypeLoader() - { } + // ReSharper disable once MemberCanBePrivate.Global + public ITypeFinder TypeFinder { get; } /// /// Gets or sets the set of assemblies to scan. @@ -115,25 +123,21 @@ namespace Umbraco.Core.Composing /// This is for unit tests. /// // internal for tests - internal IEnumerable AssembliesToScan - { - get => _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); - set => _assemblies = value; - } + public IEnumerable AssembliesToScan => _assemblies ?? (_assemblies = TypeFinder.AssembliesToScan); /// /// Gets the type lists. /// /// For unit tests. // internal for tests - internal IEnumerable TypeLists => _types.Values; + public IEnumerable TypeLists => _types.Values; /// /// Sets a type list. /// /// For unit tests. // internal for tests - internal void AddTypeList(TypeList typeList) + public void AddTypeList(TypeList typeList) { var tobject = typeof(object); // CompositeTypeTypeKey does not support null values _types[new CompositeTypeTypeKey(typeList.BaseType ?? tobject, typeList.AttributeType ?? tobject)] = typeList; @@ -180,12 +184,16 @@ namespace Umbraco.Core.Composing _currentAssembliesHash = GetFileHash(new List> { + // TODO: Would be nicer to abstract this logic out into IAssemblyHash + + // TODO: Use constants from SystemDirectories when we can (once it's ported to netstandard lib) + // the bin folder and everything in it - new Tuple(new DirectoryInfo(Current.IOHelper.MapPath(SystemDirectories.Bin)), false), + new Tuple(new DirectoryInfo(_ioHelper.MapPath("~/bin")), false), // the app code folder and everything in it - new Tuple(new DirectoryInfo(Current.IOHelper.MapPath("~/App_Code")), false), + new Tuple(new DirectoryInfo(_ioHelper.MapPath("~/App_Code")), false), // global.asax (the app domain also monitors this, if it changes will do a full restart) - new Tuple(new FileInfo(Current.IOHelper.MapPath("~/global.asax")), false) + new Tuple(new FileInfo(_ioHelper.MapPath("~/global.asax")), false) }, _logger); return _currentAssembliesHash; @@ -267,7 +275,7 @@ namespace Umbraco.Core.Composing /// A profiling logger. /// The hash. // internal for tests - internal static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) + public static string GetFileHash(IEnumerable filesAndFolders, IProfilingLogger logger) { using (logger.DebugDuration("Determining hash of code files on disk", "Hash determined")) { @@ -298,7 +306,7 @@ namespace Umbraco.Core.Composing private const int FileDeleteTimeout = 4000; // milliseconds // internal for tests - internal Attempt> TryGetCached(Type baseType, Type attributeType) + public Attempt> TryGetCached(Type baseType, Type attributeType) { var cache = _runtimeCache.GetCacheItem, IEnumerable>>(CacheKey, ReadCacheSafe, TimeSpan.FromSeconds(ListFileCacheDuration)); @@ -331,7 +339,7 @@ namespace Umbraco.Core.Composing } // internal for tests - internal Dictionary, IEnumerable> ReadCache() + public Dictionary, IEnumerable> ReadCache() { var cache = new Dictionary, IEnumerable>(); @@ -377,7 +385,7 @@ namespace Umbraco.Core.Composing } // internal for tests - internal string GetTypesListFilePath() => GetFileBasePath() + ".list"; + public string GetTypesListFilePath() => GetFileBasePath() + ".list"; private string GetTypesHashFilePath() => GetFileBasePath() + ".hash"; @@ -388,7 +396,7 @@ namespace Umbraco.Core.Composing if (_fileBasePath != null) return _fileBasePath; - _fileBasePath = Path.Combine(_localTempPath, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName); + _fileBasePath = Path.Combine(_localTempPath.FullName, "TypesCache", "umbraco-types." + NetworkHelper.FileSafeMachineName); // ensure that the folder exists var directory = Path.GetDirectoryName(_fileBasePath); @@ -402,7 +410,7 @@ namespace Umbraco.Core.Composing } // internal for tests - internal void WriteCache() + public void WriteCache() { _logger.Debug("Writing cache file."); var typesListFilePath = GetTypesListFilePath(); @@ -720,18 +728,13 @@ namespace Umbraco.Core.Composing // successfully retrieved types from the file cache: load foreach (var type in cacheResult.Result) { - try - { - // we use the build manager to ensure we get all types loaded, this is slightly slower than - // Type.GetType but if the types in the assembly aren't loaded yet it would fail whereas - // BuildManager will load them - this is how eg MVC loads types, etc - no need to make it - // more complicated - typeList.Add(BuildManager.GetType(type, true)); - } - catch (Exception ex) + var resolvedType = TypeFinder.GetTypeByName(type); + if (resolvedType != null) + typeList.Add(resolvedType); + else { // in case of any exception, we have to exit, and revert to scanning - _logger.Error(ex, "Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); + _logger.Warn("Getting {TypeName}: failed to load cache file type {CacheType}, reverting to scanning assemblies.", GetName(baseType, attributeType), type); scan = true; break; } @@ -783,7 +786,7 @@ namespace Umbraco.Core.Composing /// Represents a list of types obtained by looking for types inheriting/implementing a /// specified type, and/or marked with a specified attribute type. /// - internal class TypeList + public sealed class TypeList { private readonly HashSet _types = new HashSet(); diff --git a/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs new file mode 100644 index 0000000000..fd5b18ed39 --- /dev/null +++ b/src/Umbraco.Abstractions/Configuration/UmbracoSettings/ITypeFinderConfig.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + public interface ITypeFinderConfig + { + IEnumerable AssembliesAcceptingLoadExceptions { get; } + } +} diff --git a/src/Umbraco.Core/Manifest/IManifestFilter.cs b/src/Umbraco.Abstractions/Manifest/IManifestFilter.cs similarity index 89% rename from src/Umbraco.Core/Manifest/IManifestFilter.cs rename to src/Umbraco.Abstractions/Manifest/IManifestFilter.cs index f1e2c633c8..88e00a3966 100644 --- a/src/Umbraco.Core/Manifest/IManifestFilter.cs +++ b/src/Umbraco.Abstractions/Manifest/IManifestFilter.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; + namespace Umbraco.Core.Manifest { /// @@ -14,6 +15,6 @@ namespace Umbraco.Core.Manifest /// /// It is possible to remove, change, or add manifests. /// - void Filter(List manifests); + void Filter(List manifests); } } diff --git a/src/Umbraco.Core/Manifest/IManifestParser.cs b/src/Umbraco.Abstractions/Manifest/IManifestParser.cs similarity index 66% rename from src/Umbraco.Core/Manifest/IManifestParser.cs rename to src/Umbraco.Abstractions/Manifest/IManifestParser.cs index 367ee3be77..eeb0c756f6 100644 --- a/src/Umbraco.Core/Manifest/IManifestParser.cs +++ b/src/Umbraco.Abstractions/Manifest/IManifestParser.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Umbraco.Core.Configuration.Grid; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Manifest @@ -12,13 +11,13 @@ namespace Umbraco.Core.Manifest /// Gets all manifests, merged into a single manifest object. /// /// - IPackageManifest Manifest { get; } + PackageManifest Manifest { get; } /// /// Parses a manifest. /// - IPackageManifest ParseManifest(string text); + PackageManifest ParseManifest(string text); - IEnumerable ParseGridEditors(string text); + IEnumerable ParseGridEditors(string text); } } diff --git a/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs b/src/Umbraco.Abstractions/Manifest/ManifestContentAppDefinition.cs similarity index 92% rename from src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs rename to src/Umbraco.Abstractions/Manifest/ManifestContentAppDefinition.cs index 2aafcd8b74..35293a6377 100644 --- a/src/Umbraco.Core/Manifest/ManifestContentAppDefinition.cs +++ b/src/Umbraco.Abstractions/Manifest/ManifestContentAppDefinition.cs @@ -1,7 +1,5 @@ using System; using System.Runtime.Serialization; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; namespace Umbraco.Core.Manifest { @@ -65,11 +63,7 @@ namespace Umbraco.Core.Manifest /// Gets or sets the view for rendering the content app. /// [DataMember(Name = "view")] - public string View - { - get => _view; - set => _view = Current.IOHelper.ResolveVirtualUrl(value); - } + public string View { get; set; } /// /// Gets or sets the list of 'show' conditions for the content app. diff --git a/src/Umbraco.Abstractions/Manifest/ManifestDashboard.cs b/src/Umbraco.Abstractions/Manifest/ManifestDashboard.cs new file mode 100644 index 0000000000..2d6f96b5c2 --- /dev/null +++ b/src/Umbraco.Abstractions/Manifest/ManifestDashboard.cs @@ -0,0 +1,26 @@ +using System; +using System.ComponentModel; +using System.Runtime.Serialization; +using Umbraco.Core.Dashboards; + +namespace Umbraco.Core.Manifest +{ + [DataContract] + public class ManifestDashboard : IDashboard + { + [DataMember(Name = "alias", IsRequired = true)] + public string Alias { get; set; } + + [DataMember(Name = "weight")] + public int Weight { get; set; } = 100; + + [DataMember(Name = "view", IsRequired = true)] + public string View { get; set; } + + [DataMember(Name = "sections")] + public string[] Sections { get; set; } = Array.Empty(); + + [DataMember(Name = "access")] + public IAccessRule[] AccessRules { get; set; } = Array.Empty(); + } +} diff --git a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs b/src/Umbraco.Abstractions/Manifest/ManifestFilterCollection.cs similarity index 92% rename from src/Umbraco.Core/Manifest/ManifestFilterCollection.cs rename to src/Umbraco.Abstractions/Manifest/ManifestFilterCollection.cs index 0f49c96094..febdb7e356 100644 --- a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs +++ b/src/Umbraco.Abstractions/Manifest/ManifestFilterCollection.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Manifest /// Filters package manifests. /// /// The package manifests. - public void Filter(List manifests) + public void Filter(List manifests) { foreach (var filter in this) filter.Filter(manifests); diff --git a/src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs b/src/Umbraco.Abstractions/Manifest/ManifestFilterCollectionBuilder.cs similarity index 100% rename from src/Umbraco.Core/Manifest/ManifestFilterCollectionBuilder.cs rename to src/Umbraco.Abstractions/Manifest/ManifestFilterCollectionBuilder.cs diff --git a/src/Umbraco.Core/Manifest/ManifestSection.cs b/src/Umbraco.Abstractions/Manifest/ManifestSection.cs similarity index 100% rename from src/Umbraco.Core/Manifest/ManifestSection.cs rename to src/Umbraco.Abstractions/Manifest/ManifestSection.cs diff --git a/src/Umbraco.Core/Manifest/PackageManifest.cs b/src/Umbraco.Abstractions/Manifest/PackageManifest.cs similarity index 82% rename from src/Umbraco.Core/Manifest/PackageManifest.cs rename to src/Umbraco.Abstractions/Manifest/PackageManifest.cs index a81021432d..5e10030693 100644 --- a/src/Umbraco.Core/Manifest/PackageManifest.cs +++ b/src/Umbraco.Abstractions/Manifest/PackageManifest.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Manifest @@ -7,7 +7,8 @@ namespace Umbraco.Core.Manifest /// /// Represents the content of a package manifest. /// - public class PackageManifest : IPackageManifest + [DataContract] + public class PackageManifest { /// /// Gets the source path of the manifest. @@ -16,55 +17,55 @@ namespace Umbraco.Core.Manifest /// Gets the full absolute file path of the manifest, /// using system directory separators. /// - [JsonIgnore] + [IgnoreDataMember] public string Source { get; set; } /// /// Gets or sets the scripts listed in the manifest. /// - [JsonProperty("javascript")] + [DataMember(Name = "javascript")] public string[] Scripts { get; set; } = Array.Empty(); /// /// Gets or sets the stylesheets listed in the manifest. /// - [JsonProperty("css")] + [DataMember(Name = "css")] public string[] Stylesheets { get; set; } = Array.Empty(); /// /// Gets or sets the property editors listed in the manifest. /// - [JsonProperty("propertyEditors")] + [DataMember(Name = "propertyEditors")] public IDataEditor[] PropertyEditors { get; set; } = Array.Empty(); /// /// Gets or sets the parameter editors listed in the manifest. /// - [JsonProperty("parameterEditors")] + [DataMember(Name = "parameterEditors")] public IDataEditor[] ParameterEditors { get; set; } = Array.Empty(); /// /// Gets or sets the grid editors listed in the manifest. /// - [JsonProperty("gridEditors")] + [DataMember(Name = "gridEditors")] public GridEditor[] GridEditors { get; set; } = Array.Empty(); /// /// Gets or sets the content apps listed in the manifest. /// - [JsonProperty("contentApps")] + [DataMember(Name = "contentApps")] public ManifestContentAppDefinition[] ContentApps { get; set; } = Array.Empty(); /// /// Gets or sets the dashboards listed in the manifest. /// - [JsonProperty("dashboards")] + [DataMember(Name = "dashboards")] public ManifestDashboard[] Dashboards { get; set; } = Array.Empty(); /// /// Gets or sets the sections listed in the manifest. /// - [JsonProperty("sections")] + [DataMember(Name = "sections")] public ManifestSection[] Sections { get; set; } = Array.Empty(); } } diff --git a/src/Umbraco.Core/Models/ContentSchedule.cs b/src/Umbraco.Abstractions/Models/ContentSchedule.cs similarity index 98% rename from src/Umbraco.Core/Models/ContentSchedule.cs rename to src/Umbraco.Abstractions/Models/ContentSchedule.cs index cac4a0fd1c..4dba0456b0 100644 --- a/src/Umbraco.Core/Models/ContentSchedule.cs +++ b/src/Umbraco.Abstractions/Models/ContentSchedule.cs @@ -36,7 +36,7 @@ namespace Umbraco.Core.Models /// Gets the unique identifier of the document targeted by the scheduled action. /// [DataMember] - public Guid Id { get; internal set; } + public Guid Id { get; set; } /// /// Gets the culture of the scheduled action. diff --git a/src/Umbraco.Core/Models/ContentScheduleAction.cs b/src/Umbraco.Abstractions/Models/ContentScheduleAction.cs similarity index 100% rename from src/Umbraco.Core/Models/ContentScheduleAction.cs rename to src/Umbraco.Abstractions/Models/ContentScheduleAction.cs diff --git a/src/Umbraco.Core/Models/ContentScheduleCollection.cs b/src/Umbraco.Abstractions/Models/ContentScheduleCollection.cs similarity index 100% rename from src/Umbraco.Core/Models/ContentScheduleCollection.cs rename to src/Umbraco.Abstractions/Models/ContentScheduleCollection.cs diff --git a/src/Umbraco.Core/Models/EntityContainer.cs b/src/Umbraco.Abstractions/Models/EntityContainer.cs similarity index 98% rename from src/Umbraco.Core/Models/EntityContainer.cs rename to src/Umbraco.Abstractions/Models/EntityContainer.cs index 70f6cbd878..5c2ada7149 100644 --- a/src/Umbraco.Core/Models/EntityContainer.cs +++ b/src/Umbraco.Abstractions/Models/EntityContainer.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; -using System.Runtime.Serialization; using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Abstractions/Models/IContent.cs similarity index 100% rename from src/Umbraco.Core/Models/IContent.cs rename to src/Umbraco.Abstractions/Models/IContent.cs diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Abstractions/Models/IContentBase.cs similarity index 99% rename from src/Umbraco.Core/Models/IContentBase.cs rename to src/Umbraco.Abstractions/Models/IContentBase.cs index 0f660181fb..1864996379 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Abstractions/Models/IContentBase.cs @@ -98,7 +98,7 @@ namespace Umbraco.Core.Models /// List of properties, which make up all the data available for this Content object /// /// Properties are loaded as part of the Content object graph - PropertyCollection Properties { get; set; } + IPropertyCollection Properties { get; set; } /// /// Gets a value indicating whether the content entity has a property with the supplied alias. diff --git a/src/Umbraco.Core/Models/IDataValueEditor.cs b/src/Umbraco.Abstractions/Models/IDataValueEditor.cs similarity index 81% rename from src/Umbraco.Core/Models/IDataValueEditor.cs rename to src/Umbraco.Abstractions/Models/IDataValueEditor.cs index cb68531cc7..e095f3aa31 100644 --- a/src/Umbraco.Core/Models/IDataValueEditor.cs +++ b/src/Umbraco.Abstractions/Models/IDataValueEditor.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations; using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; -using Umbraco.Core.Services; namespace Umbraco.Core.PropertyEditors { @@ -59,12 +58,12 @@ namespace Umbraco.Core.PropertyEditors /// /// Converts a property value to a value for the editor. /// - object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null); + object ToEditor(IProperty property, string culture = null, string segment = null); // TODO: / deal with this when unplugging the xml cache // why property vs propertyType? services should be injected! etc... - IEnumerable ConvertDbToXml(Property property, IDataTypeService dataTypeService, ILocalizationService localizationService, bool published); - XNode ConvertDbToXml(PropertyType propertyType, object value, IDataTypeService dataTypeService); - string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService); + IEnumerable ConvertDbToXml(IProperty property, bool published); + XNode ConvertDbToXml(IPropertyType propertyType, object value); + string ConvertDbToString(IPropertyType propertyType, object value); } } diff --git a/src/Umbraco.Core/Models/IMedia.cs b/src/Umbraco.Abstractions/Models/IMedia.cs similarity index 100% rename from src/Umbraco.Core/Models/IMedia.cs rename to src/Umbraco.Abstractions/Models/IMedia.cs diff --git a/src/Umbraco.Abstractions/Models/IProperty.cs b/src/Umbraco.Abstractions/Models/IProperty.cs new file mode 100644 index 0000000000..cd8a07393e --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IProperty.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IProperty + { + + ValueStorageType ValueStorageType { get; } + /// + /// Returns the PropertyType, which this Property is based on + /// + IPropertyType PropertyType { get; } + + /// + /// Gets the list of values. + /// + IReadOnlyCollection Values { get; set; } + + /// + /// Returns the Alias of the PropertyType, which this Property is based on + /// + string Alias { get; } + + /// + int Id { get; set; } + + /// + Guid Key { get; set; } + + /// + DateTime CreateDate { get; set; } + + /// + DateTime UpdateDate { get; set; } + + /// + DateTime? DeleteDate { get; set; } // no change tracking - not persisted + + /// + bool HasIdentity { get; } + + /// + /// Gets the value. + /// + object GetValue(string culture = null, string segment = null, bool published = false); + + /// + /// Sets a value. + /// + void SetValue(object value, string culture = null, string segment = null); + + /// + /// Resets the entity identity. + /// + void ResetIdentity(); + + bool Equals(EntityBase other); + bool Equals(object obj); + int GetHashCode(); + object DeepClone(); + + /// + bool IsDirty(); + + /// + bool IsPropertyDirty(string propertyName); + + /// + IEnumerable GetDirtyProperties(); + + /// + /// Saves dirty properties so they can be checked with WasDirty. + void ResetDirtyProperties(); + + /// + bool WasDirty(); + + /// + bool WasPropertyDirty(string propertyName); + + /// + void ResetWereDirtyProperties(); + + /// + void ResetDirtyProperties(bool rememberDirty); + + /// + IEnumerable GetWereDirtyProperties(); + + /// + /// Disables change tracking. + /// + void DisableChangeTracking(); + + /// + /// Enables change tracking. + /// + void EnableChangeTracking(); + + int PropertyTypeId { get; } + void PublishValues(string culture = "*", string segment = "*"); + void UnpublishValues(string culture = "*", string segment = "*"); + } +} diff --git a/src/Umbraco.Abstractions/Models/IPropertyCollection.cs b/src/Umbraco.Abstractions/Models/IPropertyCollection.cs new file mode 100644 index 0000000000..ec2c4d90ca --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IPropertyCollection.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace Umbraco.Core.Models +{ + public interface IPropertyCollection : IEnumerable + { + bool TryGetValue(string propertyTypeAlias, out IProperty property); + bool Contains(string key); + + event NotifyCollectionChangedEventHandler CollectionChanged; + + void EnsurePropertyTypes(IEnumerable propertyTypes); + void EnsureCleanPropertyTypes(IEnumerable propertyTypes); + object DeepClone(); + + IProperty this[string name] { get; } + IProperty this[int index] { get; } + void Add(IProperty property); + + int Count { get; } + } +} diff --git a/src/Umbraco.Abstractions/Models/IPropertyType.cs b/src/Umbraco.Abstractions/Models/IPropertyType.cs new file mode 100644 index 0000000000..4447a858ab --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IPropertyType.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.Serialization; +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IPropertyType : IEntity + { + [DataMember] + string Name { get; } + [DataMember] + string Alias { get; } + [DataMember] + string Description { get; } + [DataMember] + int DataTypeId { get; } + [DataMember] + Guid DataTypeKey { get; } + [DataMember] + string PropertyEditorAlias { get; } + [DataMember] + ValueStorageType ValueStorageType { get; } + [DataMember] + Lazy PropertyGroupId { get; } + [DataMember] + bool Mandatory { get; } + [DataMember] + int SortOrder { get; } + [DataMember] + string ValidationRegExp { get; } + + bool SupportsPublishing { get; } + + ContentVariation Variations { get; } + + + + bool SupportsVariation(string culture, string segment, bool wildcards = false); + object ConvertAssignedValue(object value); + + + } +} diff --git a/src/Umbraco.Abstractions/Models/IPropertyValue.cs b/src/Umbraco.Abstractions/Models/IPropertyValue.cs new file mode 100644 index 0000000000..abc459a72f --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IPropertyValue.cs @@ -0,0 +1,34 @@ +namespace Umbraco.Core.Models +{ + public interface IPropertyValue + { + /// + /// Gets or sets the culture of the property. + /// + /// The culture is either null (invariant) or a non-empty string. If the property is + /// set with an empty or whitespace value, its value is converted to null. + string Culture { get; set; } + + /// + /// Gets or sets the segment of the property. + /// + /// The segment is either null (neutral) or a non-empty string. If the property is + /// set with an empty or whitespace value, its value is converted to null. + string Segment { get; set; } + + /// + /// Gets or sets the edited value of the property. + /// + object EditedValue { get; set; } + + /// + /// Gets or sets the published value of the property. + /// + object PublishedValue { get; set; } + + /// + /// Clones the property value. + /// + IPropertyValue Clone(); + } +} diff --git a/src/Umbraco.Abstractions/Models/IScript.cs b/src/Umbraco.Abstractions/Models/IScript.cs new file mode 100644 index 0000000000..9fdc321107 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IScript.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Core.Models +{ + public interface IScript : IFile + { + + } +} diff --git a/src/Umbraco.Abstractions/Models/IStylesheet.cs b/src/Umbraco.Abstractions/Models/IStylesheet.cs new file mode 100644 index 0000000000..737118d646 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IStylesheet.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IStylesheet : IFile, IRememberBeingDirty + { + /// + /// Returns a list of umbraco back office enabled stylesheet properties + /// + /// + /// An umbraco back office enabled stylesheet property has a special prefix, for example: + /// + /// /** umb_name: MyPropertyName */ p { font-size: 1em; } + /// + IEnumerable Properties { get; } + + /// + /// Adds an Umbraco stylesheet property for use in the back office + /// + /// + void AddProperty(IStylesheetProperty property); + + /// + /// Removes an Umbraco stylesheet property + /// + /// + void RemoveProperty(string name); + } +} diff --git a/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs b/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs new file mode 100644 index 0000000000..c44a147d54 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/IStylesheetProperty.cs @@ -0,0 +1,11 @@ +using Umbraco.Core.Models.Entities; + +namespace Umbraco.Core.Models +{ + public interface IStylesheetProperty : IRememberBeingDirty + { + string Alias { get; set; } + string Name { get; } + string Value { get; set; } + } +} diff --git a/src/Umbraco.Abstractions/Models/Identity/IUserLoginInfo.cs b/src/Umbraco.Abstractions/Models/Identity/IUserLoginInfo.cs new file mode 100644 index 0000000000..84dc1da7e0 --- /dev/null +++ b/src/Umbraco.Abstractions/Models/Identity/IUserLoginInfo.cs @@ -0,0 +1,13 @@ +namespace Umbraco.Core.Models.Identity +{ + public interface IUserLoginInfo + { + /// + /// Provider for the linked login, i.e. Facebook, Google, etc. + /// + string LoginProvider { get; set; } + + /// User specific key for the login provider + string ProviderKey { get; set; } + } +} diff --git a/src/Umbraco.Abstractions/Models/Script.cs b/src/Umbraco.Abstractions/Models/Script.cs index b6e49f72ee..be96c04ddd 100644 --- a/src/Umbraco.Abstractions/Models/Script.cs +++ b/src/Umbraco.Abstractions/Models/Script.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class Script : File + public class Script : File, IScript { public Script(string path) : this(path, (Func) null) @@ -25,9 +25,6 @@ namespace Umbraco.Core.Models /// /// Overrides the default Entity identity check. /// - public override bool HasIdentity - { - get { return string.IsNullOrEmpty(Path) == false; } - } + public override bool HasIdentity => string.IsNullOrEmpty(Path) == false; } } diff --git a/src/Umbraco.Abstractions/Models/Stylesheet.cs b/src/Umbraco.Abstractions/Models/Stylesheet.cs index df5786a340..48f00a1650 100644 --- a/src/Umbraco.Abstractions/Models/Stylesheet.cs +++ b/src/Umbraco.Abstractions/Models/Stylesheet.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class Stylesheet : File + public class Stylesheet : File, IStylesheet { public Stylesheet(string path) : this(path, null) @@ -120,7 +120,7 @@ namespace Umbraco.Core.Models /// /** umb_name: MyPropertyName */ p { font-size: 1em; } /// [IgnoreDataMember] - public IEnumerable Properties + public IEnumerable Properties { get { return _properties.Value; } } @@ -129,7 +129,7 @@ namespace Umbraco.Core.Models /// Adds an Umbraco stylesheet property for use in the back office /// /// - public void AddProperty(StylesheetProperty property) + public void AddProperty(IStylesheetProperty property) { if (Properties.Any(x => x.Name.InvariantEquals(property.Name))) { diff --git a/src/Umbraco.Abstractions/Models/StylesheetProperty.cs b/src/Umbraco.Abstractions/Models/StylesheetProperty.cs index 089f89deb6..bc895113bc 100644 --- a/src/Umbraco.Abstractions/Models/StylesheetProperty.cs +++ b/src/Umbraco.Abstractions/Models/StylesheetProperty.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Models /// [Serializable] [DataContract(IsReference = true)] - public class StylesheetProperty : BeingDirtyBase, IValueObject + public class StylesheetProperty : BeingDirtyBase, IValueObject, IStylesheetProperty { private string _alias; private string _value; diff --git a/src/Umbraco.Core/Persistence/IQueryRepository.cs b/src/Umbraco.Abstractions/Persistence/IQueryRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/IQueryRepository.cs rename to src/Umbraco.Abstractions/Persistence/IQueryRepository.cs diff --git a/src/Umbraco.Core/Persistence/IReadRepository.cs b/src/Umbraco.Abstractions/Persistence/IReadRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/IReadRepository.cs rename to src/Umbraco.Abstractions/Persistence/IReadRepository.cs diff --git a/src/Umbraco.Core/Persistence/IReadWriteQueryRepository.cs b/src/Umbraco.Abstractions/Persistence/IReadWriteQueryRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/IReadWriteQueryRepository.cs rename to src/Umbraco.Abstractions/Persistence/IReadWriteQueryRepository.cs diff --git a/src/Umbraco.Core/Persistence/IRepository.cs b/src/Umbraco.Abstractions/Persistence/IRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/IRepository.cs rename to src/Umbraco.Abstractions/Persistence/IRepository.cs diff --git a/src/Umbraco.Core/Persistence/IWriteRepository.cs b/src/Umbraco.Abstractions/Persistence/IWriteRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/IWriteRepository.cs rename to src/Umbraco.Abstractions/Persistence/IWriteRepository.cs diff --git a/src/Umbraco.Core/Persistence/Querying/IQuery.cs b/src/Umbraco.Abstractions/Persistence/Querying/IQuery.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Querying/IQuery.cs rename to src/Umbraco.Abstractions/Persistence/Querying/IQuery.cs diff --git a/src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs b/src/Umbraco.Abstractions/Persistence/Querying/StringPropertyMatchType.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Querying/StringPropertyMatchType.cs rename to src/Umbraco.Abstractions/Persistence/Querying/StringPropertyMatchType.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IAuditEntryRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IAuditEntryRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IAuditEntryRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IAuditEntryRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IAuditRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IAuditRepository.cs similarity index 96% rename from src/Umbraco.Core/Persistence/Repositories/IAuditRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IAuditRepository.cs index b2dd6a3297..fbd9ec2e13 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IAuditRepository.cs +++ b/src/Umbraco.Abstractions/Persistence/Repositories/IAuditRepository.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; namespace Umbraco.Core.Persistence.Repositories diff --git a/src/Umbraco.Core/Persistence/Repositories/IConsentRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IConsentRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IConsentRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IConsentRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IDataTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IDataTypeContainerRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IDictionaryRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IDictionaryRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IDictionaryRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IDocumentTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IDocumentTypeContainerRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IDomainRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IDomainRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IDomainRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IDomainRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IEntityContainerRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IEntityContainerRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IExternalLoginRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IExternalLoginRepository.cs similarity index 71% rename from src/Umbraco.Core/Persistence/Repositories/IExternalLoginRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IExternalLoginRepository.cs index 6d145e9961..ed6f2e4fb1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IExternalLoginRepository.cs +++ b/src/Umbraco.Abstractions/Persistence/Repositories/IExternalLoginRepository.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -using Microsoft.AspNet.Identity; using Umbraco.Core.Models.Identity; namespace Umbraco.Core.Persistence.Repositories { public interface IExternalLoginRepository : IReadWriteQueryRepository { - void SaveUserLogins(int memberId, IEnumerable logins); + void SaveUserLogins(int memberId, IEnumerable logins); void DeleteUserLogins(int memberId); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/ILanguageRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/ILanguageRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IMacroRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IMacroRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IMediaTypeContainerRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IMediaTypeContainerRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IMemberGroupRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IMemberGroupRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IMemberGroupRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/INotificationsRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/INotificationsRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/INotificationsRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/INotificationsRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IPartialViewMacroRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewMacroRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IPartialViewMacroRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewMacroRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IPartialViewRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IPartialViewRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IRedirectUrlRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IRedirectUrlRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IRelationRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IRelationRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IRelationTypeRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IRelationTypeRepository.cs similarity index 100% rename from src/Umbraco.Core/Persistence/Repositories/IRelationTypeRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IRelationTypeRepository.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs b/src/Umbraco.Abstractions/Persistence/Repositories/IScriptRepository.cs similarity index 66% rename from src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs rename to src/Umbraco.Abstractions/Persistence/Repositories/IScriptRepository.cs index ea88cb7618..70226777b5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IScriptRepository.cs +++ b/src/Umbraco.Abstractions/Persistence/Repositories/IScriptRepository.cs @@ -3,11 +3,14 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - public interface IScriptRepository : IReadRepository, IWriteRepository