From 923f4f517fd941f289e5c14baca5068c62a85beb Mon Sep 17 00:00:00 2001 From: "shannon@ShandemVaio" Date: Wed, 1 Aug 2012 23:30:37 +0600 Subject: [PATCH] Created ActionsResolver for creating IActions and updated Action class to use this now. Still needs a bit of cleanup, see TODO notes in Action.cs --- src/Umbraco.Core/CoreBootManager.cs | 3 + src/Umbraco.Core/PluginManager.cs | 9 + src/Umbraco.Core/Resolving/ActionsResolver.cs | 58 ++++++ .../Resolving/ManyObjectsResolverBase.cs | 13 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/ActionsResolverTests.cs | 182 ++++++++++++++++++ src/Umbraco.Tests/DataTypeFactoryTests.cs | 1 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/umbraco.cms/Actions/Action.cs | 84 +++----- 9 files changed, 295 insertions(+), 57 deletions(-) create mode 100644 src/Umbraco.Core/Resolving/ActionsResolver.cs create mode 100644 src/Umbraco.Tests/ActionsResolverTests.cs diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 87b2f06a8d..738def070a 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -84,6 +84,9 @@ namespace Umbraco.Core PackageActionsResolver.Current = new PackageActionsResolver( PluginManager.Current.ResolvePackageActions()); + + ActionsResolver.Current = new ActionsResolver( + PluginManager.Current.ResolveActions()); } } } diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 04a2ce1e74..a809db1e6c 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -94,6 +94,15 @@ namespace Umbraco.Core return ResolveTypes(); } + /// + /// Returns all available IAction in application + /// + /// + internal IEnumerable ResolveActions() + { + return ResolveTypes(); + } + /// /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set /// this will search all assemblies known to have plugins and exclude ones known to not have them. diff --git a/src/Umbraco.Core/Resolving/ActionsResolver.cs b/src/Umbraco.Core/Resolving/ActionsResolver.cs new file mode 100644 index 0000000000..8dfc274dae --- /dev/null +++ b/src/Umbraco.Core/Resolving/ActionsResolver.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using umbraco.interfaces; + +namespace Umbraco.Core.Resolving +{ + /// + /// A resolver to return all IAction objects + /// + internal sealed class ActionsResolver : ManyObjectsResolverBase + { + + /// + /// Constructor + /// + /// + internal ActionsResolver(IEnumerable packageActions) + : base(packageActions) + { + + } + + /// + /// Gets the implementations. + /// + public IEnumerable Actions + { + get + { + return Values; + } + } + + protected override IEnumerable CreateInstances() + { + var actions = new List(); + var foundIActions = PluginManager.Current.ResolveActions(); + foreach (var type in foundIActions) + { + IAction typeInstance; + var instance = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); + //if the singletone initializer is not found, try simply creating an instance of the IAction if it supports public constructors + if (instance == null) + typeInstance = PluginManager.Current.CreateInstance(type); + else + typeInstance = instance.GetValue(null, null) as IAction; + + if (typeInstance != null) + { + actions.Add(typeInstance); + } + } + return actions; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Resolving/ManyObjectsResolverBase.cs b/src/Umbraco.Core/Resolving/ManyObjectsResolverBase.cs index 77e6d9ae24..936cfec2da 100644 --- a/src/Umbraco.Core/Resolving/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/Resolving/ManyObjectsResolverBase.cs @@ -107,8 +107,7 @@ namespace Umbraco.Core.Resolving { l.UpgradeToWriteLock(); //add the items to the context items (based on full type name) - CurrentHttpContext.Items[this.GetType().FullName] = new List( - PluginManager.Current.CreateInstances(InstanceTypes)); + CurrentHttpContext.Items[this.GetType().FullName] = new List(CreateInstances()); } return (List)CurrentHttpContext.Items[this.GetType().FullName]; } @@ -119,19 +118,23 @@ namespace Umbraco.Core.Resolving if (_applicationInstances == null) { l.UpgradeToWriteLock(); - _applicationInstances = new List( - PluginManager.Current.CreateInstances(InstanceTypes)); + _applicationInstances = new List(CreateInstances()); } return _applicationInstances; } case ObjectLifetimeScope.Transient: default: //create new instances each time - return PluginManager.Current.CreateInstances(InstanceTypes); + return CreateInstances(); } } } + protected virtual IEnumerable CreateInstances() + { + return PluginManager.Current.CreateInstances(InstanceTypes); + } + /// /// Removes a type. /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index b3948db2e2..c9d3889ce2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -51,6 +51,7 @@ + diff --git a/src/Umbraco.Tests/ActionsResolverTests.cs b/src/Umbraco.Tests/ActionsResolverTests.cs new file mode 100644 index 0000000000..eac2ee21dc --- /dev/null +++ b/src/Umbraco.Tests/ActionsResolverTests.cs @@ -0,0 +1,182 @@ +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Resolving; +using Umbraco.Tests.TestHelpers; +using umbraco.BasePages; +using umbraco.interfaces; + +namespace Umbraco.Tests +{ + [TestFixture] + public class ActionsResolverTests + { + [SetUp] + public void Initialize() + { + TestHelper.SetupLog4NetForTests(); + + //this ensures its reset + PluginManager.Current = new PluginManager(); + + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + PluginManager.Current.AssembliesToScan = new[] + { + this.GetType().Assembly + }; + + ActionsResolver.Current = new ActionsResolver( + PluginManager.Current.ResolveActions()); + + Resolution.Freeze(); + } + + [TearDown] + public void TearDown() + { + ActionsResolver.Reset(); + Resolution.IsFrozen = false; + } + + [Test] + public void Create_Types() + { + var found = ActionsResolver.Current.Actions; + Assert.AreEqual(2, found.Count()); + } + + #region Classes for tests + public class SingletonAction : IAction + { + //create singleton + private static readonly SingletonAction instance = new SingletonAction(); + + public static SingletonAction Instance + { + get { return instance; } + } + + #region IAction Members + + public char Letter + { + get + { + return 'I'; + } + } + + public string JsFunctionName + { + get + { + return string.Format("{0}.actionAssignDomain()", ClientTools.Scripts.GetAppActions); + } + } + + public string JsSource + { + get + { + return null; + } + } + + public string Alias + { + get + { + return "assignDomain"; + } + } + + public string Icon + { + get + { + return ".sprDomain"; + } + } + + public bool ShowInNotifier + { + get + { + return false; + } + } + public bool CanBePermissionAssigned + { + get + { + return true; + } + } + #endregion + } + + public class NonSingletonAction : IAction + { + #region IAction Members + + public char Letter + { + get + { + return 'Q'; + } + } + + public string JsFunctionName + { + get + { + return string.Format("{0}.actionAssignDomain()", ClientTools.Scripts.GetAppActions); + } + } + + public string JsSource + { + get + { + return null; + } + } + + public string Alias + { + get + { + return "asfasdf"; + } + } + + public string Icon + { + get + { + return ".sprDomain"; + } + } + + public bool ShowInNotifier + { + get + { + return false; + } + } + public bool CanBePermissionAssigned + { + get + { + return true; + } + } + #endregion + } + + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/DataTypeFactoryTests.cs b/src/Umbraco.Tests/DataTypeFactoryTests.cs index 58c5785648..b781432735 100644 --- a/src/Umbraco.Tests/DataTypeFactoryTests.cs +++ b/src/Umbraco.Tests/DataTypeFactoryTests.cs @@ -9,6 +9,7 @@ using System.Linq; namespace Umbraco.Tests { + [TestFixture] public class DataTypeFactoryTests { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index f86945549e..ae48d1eb14 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -52,6 +52,7 @@ + diff --git a/src/umbraco.cms/Actions/Action.cs b/src/umbraco.cms/Actions/Action.cs index 65bbedfaab..1ed6d33f39 100644 --- a/src/umbraco.cms/Actions/Action.cs +++ b/src/umbraco.cms/Actions/Action.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Web; using System.Reflection; using Umbraco.Core; +using Umbraco.Core.Resolving; using umbraco.BasePages; using umbraco.BusinessLogic.Utils; using umbraco.cms; @@ -11,6 +12,7 @@ using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.workflow; using umbraco.interfaces; using System.Text.RegularExpressions; +using System.Linq; namespace umbraco.BusinessLogic.Actions { @@ -29,10 +31,7 @@ namespace umbraco.BusinessLogic.Actions /// public class Action { - private static readonly List Actions = new List(); private static readonly List ActionHandlers = new List(); - - private static readonly List ActionJsReference = new List(); private static readonly Dictionary ActionJs = new Dictionary(); private static readonly object Lock = new object(); @@ -42,20 +41,29 @@ namespace umbraco.BusinessLogic.Actions ReRegisterActionsAndHandlers(); } - /// - /// This is used when an IAction or IActionHandler is installed into the system - /// and needs to be loaded into memory. - /// - public static void ReRegisterActionsAndHandlers() - { - lock (Lock) - { - Actions.Clear(); - ActionHandlers.Clear(); - RegisterIActions(); - RegisterIActionHandlers(); - } - } + /// + /// This is used when an IAction or IActionHandler is installed into the system + /// and needs to be loaded into memory. + /// + /// + /// TODO: this shouldn't be needed... we should restart the app pool when a package is installed! + /// + public static void ReRegisterActionsAndHandlers() + { + lock (Lock) + { + + //TODO: Based on the above, this is a big hack as types should all be cleared on package install! + ActionsResolver.Reset(); + ActionHandlers.Clear(); + + //TODO: Based on the above, this is a big hack as types should all be cleared on package install! + ActionsResolver.Current = new ActionsResolver( + TypeFinder2.FindClassesOfType(PluginManager.Current.AssembliesToScan)); + + RegisterIActionHandlers(); + } + } /// /// Stores all IActionHandlers that have been loaded into memory into a list @@ -71,36 +79,6 @@ namespace umbraco.BusinessLogic.Actions } - /// - /// Stores all IActions that have been loaded into memory into a list - /// - private static void RegisterIActions() - { - - if (Actions.Count == 0) - { - var foundIActions = PluginManager.Current.ResolveActions(); - foreach (var type in foundIActions) - { - IAction typeInstance; - var instance = type.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static); - //if the singletone initializer is not found, try simply creating an instance of the IAction if it supports public constructors - if (instance == null) - typeInstance = PluginManager.Current.CreateInstance(type); - else - typeInstance = instance.GetValue(null, null) as IAction; - - if (typeInstance != null) - { - if (!string.IsNullOrEmpty(typeInstance.JsSource)) - ActionJsReference.Add(typeInstance.JsSource); - Actions.Add(typeInstance); - } - } - } - - } - /// /// Whenever an action is performed upon a document/media/member, this method is executed, ensuring that /// all registered handlers will have an oppotunity to handle the action. @@ -169,7 +147,8 @@ namespace umbraco.BusinessLogic.Actions /// public static List GetJavaScriptFileReferences() { - return ActionJsReference; + return ActionsResolver.Current.Actions.Select(x => x.JsSource).ToList(); + //return ActionJsReference; } /// @@ -186,7 +165,7 @@ namespace umbraco.BusinessLogic.Actions { string _actionJsList = ""; - foreach (IAction action in Actions) + foreach (IAction action in ActionsResolver.Current.Actions) { // Adding try/catch so this rutine doesn't fail if one of the actions fail // Add to language JsList @@ -223,9 +202,10 @@ namespace umbraco.BusinessLogic.Actions /// /// /// An arraylist containing all javascript variables for the contextmenu in the tree + [Obsolete("Use ActionsResolver.Current.Actions instead")] public static ArrayList GetAll() { - return new ArrayList(Actions); + return new ArrayList(ActionsResolver.Current.Actions.ToList()); } /// @@ -239,7 +219,7 @@ namespace umbraco.BusinessLogic.Actions List list = new List(); foreach (char c in actions.ToCharArray()) { - IAction action = Actions.Find( + IAction action = ActionsResolver.Current.Actions.ToList().Find( delegate(IAction a) { return a.Letter == c; @@ -267,7 +247,7 @@ namespace umbraco.BusinessLogic.Actions /// public static List GetPermissionAssignable() { - return Actions.FindAll( + return ActionsResolver.Current.Actions.ToList().FindAll( delegate(IAction a) { return (a.CanBePermissionAssigned);