diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 229c77c8d8..b8edf6a319 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -23,4 +23,5 @@ using System.Runtime.InteropServices; [assembly: InternalsVisibleTo("businesslogic")] [assembly: InternalsVisibleTo("cms")] [assembly: InternalsVisibleTo("umbraco.editorControls")] +[assembly: InternalsVisibleTo("umbraco.MacroEngines")] diff --git a/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs b/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs index 6aac79de18..47dfa296a3 100644 --- a/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs +++ b/src/Umbraco.Core/Resolving/ManyObjectResolverBase.cs @@ -1,10 +1,9 @@ +using System; using System.Collections.Generic; using System.Threading; namespace Umbraco.Core.Resolving { - //NOTE: This class should also support creating instances of the object and managing their lifespan, - // for example, some resolvers would want to return new object each time, per request or per application lifetime. /// /// A Resolver which manages an ordered list of objects. @@ -33,7 +32,7 @@ namespace Umbraco.Core.Resolving /// /// Initializes a new instance of the class with an initial list of objects. /// - /// The list of objects. + /// The list of objects. protected ManyObjectResolverBase(IEnumerable value) { _resolved = new List(value); @@ -67,9 +66,15 @@ namespace Umbraco.Core.Resolving public void Add(TResolved value) { Resolution.EnsureNotFrozen(); - using (new WriteLock(Lock)) + using (var l = new UpgradeableReadLock(Lock)) { - _resolved.Add(value); + if (_resolved.Contains(value)) + { + throw new InvalidOperationException("The object " + value + " already exists in the collection"); + }; + + l.UpgradeToWriteLock(); + _resolved.Add(value); } } @@ -93,8 +98,14 @@ namespace Umbraco.Core.Resolving public void Insert(int index, TResolved value) { Resolution.EnsureNotFrozen(); - using (new WriteLock(Lock)) + using (var l = new UpgradeableReadLock(Lock)) { + if (_resolved.Contains(value)) + { + throw new InvalidOperationException("The object " + value + " already exists in the collection"); + }; + + l.UpgradeToWriteLock(); _resolved.Insert(index, value); } } diff --git a/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs b/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs deleted file mode 100644 index 14161df9ec..0000000000 --- a/src/Umbraco.Core/Resolving/ManyWeightedResolved.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Resolving -{ - //internal class ManyWeightedResolved - //{ - // List _resolved = new List(); - // Dictionary _weights = new Dictionary(); - - // public ManyWeightedResolved() - // { - // Resolution.Frozen += (sender, e) => - // { - // _resolved = _resolved.OrderBy(r => _weights[r.GetType()]).ToList(); - // _weights = null; - // }; - // } - - // public ManyWeightedResolved(IEnumerable resolved) - // : this() - // { - // _resolved.AddRange(resolved); - // foreach (var type in _resolved.Select(r => r.GetType())) - // _weights.Add(type, ResolutionWeightAttribute.ReadWeight(type)); - // } - - // public IEnumerable Values - // { - // get { return _resolved; } - // } - - // #region Manage collection - - // public void Add(TResolved value) - // { - // Resolution.EnsureNotFrozen(); - - // var type = value.GetType(); - // EnsureNotExists(type); - // _resolved.Add(value); - // _weights[type] = ResolutionWeightAttribute.ReadWeight(type); - // } - - // public void Add(TResolved value, int weight) - // { - // Resolution.EnsureNotFrozen(); - - // var type = value.GetType(); - // EnsureNotExists(type); - // _resolved.Add(value); - // _weights[type] = weight; - // } - - // public void AddRange(IEnumerable values) - // { - // Resolution.EnsureNotFrozen(); - - // foreach (var value in values) - // { - // var type = value.GetType(); - // EnsureNotExists(type); - // _resolved.Add(value); - // _weights[type] = ResolutionWeightAttribute.ReadWeight(type); - // } - // } - - // //public void SetWeight(TResolved value, int weight) - // //{ - // // Resolution.EnsureNotFrozen(); - - // // var type = value.GetType(); - // // EnsureExists(type); - // // _weights[type] = weight; - // //} - - // public void SetWeight(int weight) - // { - // Resolution.EnsureNotFrozen(); - - // var type = typeof(TResolving); - // EnsureExists(type); - // _weights[type] = weight; - // } - - // //public int GetWeight(TResolved value) - // //{ - // // var type = value.GetType(); - // // EnsureExists(type); - // // return _weights[value.GetType()]; - // //} - - // public int GetWeight() - // { - // var type = typeof(TResolving); - // EnsureExists(type); - // return _weights[type]; - // } - - // //public void Remove(TResolved value) - // //{ - // // Resolution.EnsureNotFrozen(); - - // // var type = value.GetType(); - // // var remove = _resolved.SingleOrDefault(r => r.GetType() == type); - // // if (remove != null) - // // { - // // _resolved.Remove(remove); - // // _weights.Remove(remove.GetType()); - // // } - // //} - - // public void Remove() - // { - // Resolution.EnsureNotFrozen(); - - // var type = typeof(TResolving); - // var remove = _resolved.SingleOrDefault(r => r.GetType() == type); - // if (remove != null) - // { - // _resolved.Remove(remove); - // _weights.Remove(remove.GetType()); - // } - // } - - // public void Clear() - // { - // Resolution.EnsureNotFrozen(); - - // _resolved = new List(); - // _weights = new Dictionary(); - // } - - // #endregion - - // #region Utilities - - // public bool Exists(Type type) - // { - // return _resolved.Any(r => r.GetType() == type); - // } - - // void EnsureExists(Type type) - // { - // if (!Exists(type)) - // throw new InvalidOperationException("There is not value of that type in the collection."); - // } - - // void EnsureNotExists(Type type) - // { - // if (Exists(type)) - // throw new InvalidOperationException("A value of that type already exists in the collection."); - // } - - // #endregion - //} -} diff --git a/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs b/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs deleted file mode 100644 index 061f8423b2..0000000000 --- a/src/Umbraco.Core/Resolving/ManyWeightedResolverBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Resolving -{ - //internal abstract class ManyWeightedResolverBase : ResolverBase - // where TResolver : class - //{ - // readonly ManyWeightedResolved _resolved; - - // protected ManyWeightedResolverBase() - // { - // _resolved = new ManyWeightedResolved(); - // } - - // protected ManyWeightedResolverBase(IEnumerable values) - // { - // _resolved = new ManyWeightedResolved(values); - // } - - // protected IEnumerable Values - // { - // get { return _resolved.Values; } - // } - - // #region Manage collection - - // public void Add(TResolved value) - // { - // _resolved.Add(value); - // } - - // public void Add(TResolved value, int weight) - // { - // _resolved.Add(value, weight); - // } - - // public void AddRange(IEnumerable values) - // { - // _resolved.AddRange(values); - // } - - // public void SetWeight(int weight) - // { - // _resolved.SetWeight(weight); - // } - - // public int GetWeight() - // { - // return _resolved.GetWeight(); - // } - - // public void Remove() - // { - // _resolved.Remove(); - // } - - // public void Clear() - // { - // _resolved.Clear(); - // } - - // #endregion - //} -} diff --git a/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs b/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs deleted file mode 100644 index c888798513..0000000000 --- a/src/Umbraco.Core/Resolving/ResolutionWeightAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Umbraco.Core.Resolving -{ - //[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - //internal class ResolutionWeightAttribute : Attribute - //{ - // public const int DefaultWeight = 100; - - // public ResolutionWeightAttribute(int weight) - // { - // this.Weight = weight; - // } - - // public int Weight { get; private set; } - - // public static int ReadWeight(Type type) - // { - // var attr = type.GetCustomAttribute(false); - // return attr != null ? attr.Weight : DefaultWeight; - // } - //} -} diff --git a/src/Umbraco.Core/Resolving/SingleResolved.cs b/src/Umbraco.Core/Resolving/SingleResolved.cs deleted file mode 100644 index 6ebca480e6..0000000000 --- a/src/Umbraco.Core/Resolving/SingleResolved.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; - -namespace Umbraco.Core.Resolving -{ - internal class SingleResolved where TResolved : class - { - TResolved _resolved; - readonly bool _canBeNull; - - public SingleResolved() - : this(false) - { } - - public SingleResolved(TResolved value) - : this(false) - { - _resolved = value; - } - - public SingleResolved(bool canBeNull) - { - _canBeNull = canBeNull; - } - - public SingleResolved(TResolved value, bool canBeNull) - { - _resolved = value; - _canBeNull = canBeNull; - } - - public TResolved Value - { - get - { - return _resolved; - } - - set - { - Resolution.EnsureNotFrozen(); - - if (!_canBeNull && value == null) - throw new ArgumentNullException("value"); - _resolved = value; - } - } - } -} diff --git a/src/Umbraco.Core/TypeFinder2.cs b/src/Umbraco.Core/TypeFinder2.cs index d5641be4a5..82360666a8 100644 --- a/src/Umbraco.Core/TypeFinder2.cs +++ b/src/Umbraco.Core/TypeFinder2.cs @@ -179,10 +179,7 @@ namespace Umbraco.Core "Umbraco.Core,", "umbraco.datalayer,", "umbraco.editorControls,", - "umbraco.interfaces,", - "umbraco.MacroEngines,", - "umbraco.MacroEngines.", - "umbraco.macroRenderings,", + "umbraco.interfaces,", "umbraco.providers,", "Umbraco.Web.UI,", "umbraco.webservices", diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3106a9d6b3..48f14060e4 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -67,13 +67,9 @@ - - - - diff --git a/src/Umbraco.Tests/PluginTypeResolverTests.cs b/src/Umbraco.Tests/PluginTypeResolverTests.cs index 058c71cb31..e0fc8f60df 100644 --- a/src/Umbraco.Tests/PluginTypeResolverTests.cs +++ b/src/Umbraco.Tests/PluginTypeResolverTests.cs @@ -42,7 +42,8 @@ namespace Umbraco.Tests typeof(DLRScriptingEngine).Assembly, typeof(ICultureDictionary).Assembly, typeof(UmbracoContext).Assembly, - typeof(BaseDataType).Assembly + typeof(BaseDataType).Assembly, + typeof(DynamicNode).Assembly }; } @@ -89,6 +90,13 @@ namespace Umbraco.Tests Assert.AreEqual(33, types.Count()); } + [Test] + public void Resolves_RazorDataTypeModels() + { + var types = PluginTypeResolver.Current.ResolveRazorDataTypeModels(); + Assert.AreEqual(3, types.Count()); + } + public interface IFindMe { diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 181f37cb5f..1cd89bf46c 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -127,7 +127,11 @@ - + + + + + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c2bf75bae1..3ae019fe37 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -1817,7 +1817,9 @@ ASPXCodeBehind - + + ASPXCodeBehind + diff --git a/src/umbraco.MacroEngines/PluginTypeResolverExtensions.cs b/src/umbraco.MacroEngines/PluginTypeResolverExtensions.cs new file mode 100644 index 0000000000..8a6383af9b --- /dev/null +++ b/src/umbraco.MacroEngines/PluginTypeResolverExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using umbraco.interfaces; + +namespace umbraco.MacroEngines +{ + /// + /// Extension methods for the PluginTypeResolver + /// + public static class PluginTypeResolverExtensions + { + + /// + /// Returns all available IMacroGuiRendering in application + /// + /// + /// + internal static IEnumerable ResolveRazorDataTypeModels(this PluginTypeResolver resolver) + { + return resolver.ResolveTypesWithAttribute(); + } + + + } +} \ No newline at end of file diff --git a/src/umbraco.MacroEngines/Properties/AssemblyInfo.cs b/src/umbraco.MacroEngines/Properties/AssemblyInfo.cs index 208cc4509b..ead4294796 100644 --- a/src/umbraco.MacroEngines/Properties/AssemblyInfo.cs +++ b/src/umbraco.MacroEngines/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -19,4 +20,5 @@ using System.Runtime.InteropServices; // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("207a5ae9-5f35-4dec-a649-d3cf4d0efbd9")] +[assembly: InternalsVisibleTo("Umbraco.Tests")] diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs index 3ab7ea1d38..145a1f7d75 100644 --- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs +++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Dynamic; using System.Linq; +using System.Threading; using System.Web; +using Umbraco.Core; using umbraco.interfaces; using System.Collections; using System.Reflection; @@ -430,7 +432,71 @@ namespace umbraco.MacroEngines } return list; } - static Dictionary, Type> RazorDataTypeModelTypes = null; + + private static Dictionary, Type> _razorDataTypeModelTypes = null; + private static readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); + + internal static Dictionary, Type> RazorDataTypeModelTypes + { + get + { + using (var l = new UpgradeableReadLock(_locker)) + { + if (_razorDataTypeModelTypes == null) + { + l.UpgradeToWriteLock(); + + var foundTypes = new Dictionary, Type>(); + + HttpContext.Current.Trace.Write("RazorDataTypeModelTypes cache is empty, populating cache using PluginTypeResolver..."); + try + { + PluginTypeResolver.Current.ResolveRazorDataTypeModels() + .ToList() + .ConvertAll(type => + { + var razorDataTypeModelAttributes = type.GetCustomAttributes(true); + return razorDataTypeModelAttributes.ToList().ConvertAll(razorDataTypeModelAttribute => + { + var g = razorDataTypeModelAttribute.DataTypeEditorId; + var priority = razorDataTypeModelAttribute.Priority; + return new KeyValuePair, Type>(new System.Tuple(g, priority), type); + }); + }) + .SelectMany(item => item) + .ToList() + .ForEach(item => + { + System.Tuple key = item.Key; + if (!foundTypes.ContainsKey(key)) + { + foundTypes.Add(key, item.Value); + } + }); + HttpContext.Current.Trace.Write(string.Format("{0} items added to cache...", foundTypes.Count)); + var i = 1; + foreach (var item in foundTypes) + { + HttpContext.Current.Trace.Write(string.Format("{0}/{1}: {2}@{4} => {3}", i, foundTypes.Count, item.Key.Item1, item.Value.FullName, item.Key.Item2)); + i++; + } + + //there is no error, so set the collection + _razorDataTypeModelTypes = foundTypes; + + } + catch (Exception ex) + { + HttpContext.Current.Trace.Warn("Exception occurred while populating cache, will keep RazorDataTypeModelTypes to null so that this error remains visible and you don't end up with an empty cache with silent failure."); + HttpContext.Current.Trace.Warn(string.Format("The exception was {0} and the message was {1}. {2}", ex.GetType().FullName, ex.Message, ex.StackTrace)); + } + + } + return _razorDataTypeModelTypes; + } + } + } + public override bool TryGetMember(GetMemberBinder binder, out object result) { @@ -472,57 +538,11 @@ namespace umbraco.MacroEngines //contextAlias is the node which the property data was returned from Guid dataType = ContentType.GetDataType(data.ContextAlias, data.Alias); HttpContext.Current.Trace.Write(string.Format("RazorDynamicNode got datatype {0} for {1} on {2}", dataType, data.Alias, data.ContextAlias)); - if (RazorDataTypeModelTypes == null) - { - HttpContext.Current.Trace.Write("RazorDataTypeModelTypes cache is empty, populating cache using TypeFinder..."); - try - { - RazorDataTypeModelTypes = new Dictionary, Type>(); - - var typeFinder = new Umbraco.Core.TypeFinder2(); - - typeFinder.FindClassesWithAttribute() - .ToList() - .FindAll(type => typeof(IRazorDataTypeModel).IsAssignableFrom(type)) - .ConvertAll(type => - { - IEnumerable RazorDataTypeModelAttributes = Attribute.GetCustomAttributes(type, typeof(RazorDataTypeModel)).Cast(); - return RazorDataTypeModelAttributes.ToList().ConvertAll(RazorDataTypeModelAttribute => - { - Guid g = RazorDataTypeModelAttribute.DataTypeEditorId; - int priority = RazorDataTypeModelAttribute.Priority; - return new KeyValuePair, Type>(new System.Tuple(g, priority), type); - }); - }) - .SelectMany(item => item) - .ToList() - .ForEach(item => - { - System.Tuple key = item.Key; - if (!RazorDataTypeModelTypes.ContainsKey(key)) - { - RazorDataTypeModelTypes.Add(key, item.Value); - } - }); - HttpContext.Current.Trace.Write(string.Format("{0} items added to cache...", RazorDataTypeModelTypes.Count)); - int i = 1; - foreach (KeyValuePair, Type> item in RazorDataTypeModelTypes) - { - HttpContext.Current.Trace.Write(string.Format("{0}/{1}: {2}@{4} => {3}", i, RazorDataTypeModelTypes.Count, item.Key.Item1, item.Value.FullName, item.Key.Item2)); - i++; - } - } - catch (Exception ex) - { - HttpContext.Current.Trace.Warn("Exception occurred while populating cache, Will set RazorDataTypeModelTypes to null so that this error remains visible and you don't end up with an empty cache with silent failure."); - HttpContext.Current.Trace.Warn(string.Format("The exception was {0} and the message was {1}. {2}", ex.GetType().FullName, ex.Message, ex.StackTrace)); - RazorDataTypeModelTypes = null; - } - } + HttpContext.Current.Trace.Write(string.Format("Checking for a RazorDataTypeModel for data type guid {0}...", dataType)); HttpContext.Current.Trace.Write("Checking the RazorDataTypeModelTypes static mappings to see if there is a static mapping..."); - RazorDataTypeModelStaticMappingItem staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping => + var staticMapping = UmbracoSettings.RazorDataTypeModelStaticMapping.FirstOrDefault(mapping => { return mapping.Applies(dataType, data.ContextAlias, data.Alias); }); @@ -555,7 +575,7 @@ namespace umbraco.MacroEngines } - if (RazorDataTypeModelTypes != null && RazorDataTypeModelTypes.Where(model => model.Key.Item1 == dataType).Any() && dataType != Guid.Empty) + if (RazorDataTypeModelTypes != null && RazorDataTypeModelTypes.Any(model => model.Key.Item1 == dataType) && dataType != Guid.Empty) { var razorDataTypeModelDefinition = RazorDataTypeModelTypes.Where(model => model.Key.Item1 == dataType).OrderByDescending(model => model.Key.Item2).FirstOrDefault(); if (!(razorDataTypeModelDefinition.Equals(default(KeyValuePair, Type>)))) diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index c8944032f3..19b2bdafac 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -77,6 +77,7 @@ Properties\SolutionInfo.cs +