From d7f2041ee984d24b8ccfdd7726d3548610b9fec5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 11 Jan 2018 18:00:42 +1100 Subject: [PATCH] Makes the TourFilterResolver a real resolver --- .../ManyObjectsResolverBase.cs | 2 +- src/Umbraco.Web/Editors/TourController.cs | 56 +++++++++++--- .../Models/BackOfficeTourFilter.cs | 61 +++++++++++++++ src/Umbraco.Web/TourFilterResolver.cs | 75 +++++++++++++++---- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/WebBootManager.cs | 2 + 6 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 src/Umbraco.Web/Models/BackOfficeTourFilter.cs diff --git a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs index ad35b81ffb..6b64172633 100644 --- a/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs +++ b/src/Umbraco.Core/ObjectResolution/ManyObjectsResolverBase.cs @@ -394,7 +394,7 @@ namespace Umbraco.Core.ObjectResolution /// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances /// created. Typically only used if a resolver is no longer used in an application and memory is to be GC'd /// - internal void ResetCollections() + internal virtual void ResetCollections() { using (new WriteLock(_lock)) { diff --git a/src/Umbraco.Web/Editors/TourController.cs b/src/Umbraco.Web/Editors/TourController.cs index 3355093659..b0677ed78e 100644 --- a/src/Umbraco.Web/Editors/TourController.cs +++ b/src/Umbraco.Web/Editors/TourController.cs @@ -23,15 +23,21 @@ namespace Umbraco.Web.Editors if (UmbracoConfig.For.UmbracoSettings().BackOffice.Tours.EnableTours == false) return result; + var filters = TourFilterResolver.Current.Filters.ToList(); + + //get all filters that will be applied to all tour aliases + var aliasOnlyFilters = filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList(); + + //don't pass in any filters for core tours that have a plugin name assigned + var nonPluginFilters = filters.Where(x => x.PluginName == null).ToList(); + //add core tour files var coreToursPath = Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours"); if (Directory.Exists(coreToursPath)) { - var coreTourFiles = Directory.GetFiles(coreToursPath, "*.json"); - - foreach (var tourFile in coreTourFiles) + foreach (var tourFile in Directory.EnumerateFiles(coreToursPath, "*.json")) { - TryParseTourFile(tourFile, result); + TryParseTourFile(tourFile, result, nonPluginFilters, aliasOnlyFilters); } } @@ -39,6 +45,14 @@ namespace Umbraco.Web.Editors foreach (var plugin in Directory.EnumerateDirectories(IOHelper.MapPath(SystemDirectories.AppPlugins))) { var pluginName = Path.GetFileName(plugin.TrimEnd('\\')); + var pluginFilters = filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList(); + + //If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely + var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null); + if (isPluginFiltered) continue; + + //combine matched package filters with filters not specific to a package + var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList(); foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice")) { @@ -46,7 +60,7 @@ namespace Umbraco.Web.Editors { foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json")) { - TryParseTourFile(tourFile, result, pluginName); + TryParseTourFile(tourFile, result, combinedFilters, aliasOnlyFilters, pluginName); } } } @@ -55,22 +69,44 @@ namespace Umbraco.Web.Editors return result.OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase); } - private void TryParseTourFile(string tourFile, List result, string pluginName = null) + private void TryParseTourFile(string tourFile, + ICollection result, + List filters, + List aliasOnlyFilters, + string pluginName = null) { + var fileName = Path.GetFileNameWithoutExtension(tourFile); + if (fileName == null) return; + + //get the filters specific to this file + var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList(); + + //If there is any filter applied to match the file only (no tour alias) then ignore the file entirely + var isFileFiltered = fileFilters.Any(x => x.TourAlias == null); + if (isFileFiltered) return; + + //now combine all aliases to filter below + var aliasFilters = aliasOnlyFilters.Concat(filters.Where(x => x.TourAlias != null)) + .Select(x => x.TourAlias) + .ToList(); + try { var contents = File.ReadAllText(tourFile); var tours = JsonConvert.DeserializeObject(contents); - var disabledTours = TourFilterResolver.Current.DisabledTours; - result.Add(new BackOfficeTourFile + var tour = new BackOfficeTourFile { FileName = Path.GetFileNameWithoutExtension(tourFile), PluginName = pluginName, Tours = tours - .Where(x => disabledTours.Contains(x.Alias, StringComparer.InvariantCultureIgnoreCase) == false) + .Where(x => aliasFilters.Count == 0 || aliasFilters.All(filter => filter.IsMatch(x.Alias)) == false) .ToArray() - }); + }; + + //don't add if all of the tours are filtered + if (tour.Tours.Any()) + result.Add(tour); } catch (IOException e) { diff --git a/src/Umbraco.Web/Models/BackOfficeTourFilter.cs b/src/Umbraco.Web/Models/BackOfficeTourFilter.cs new file mode 100644 index 0000000000..994cdb6d29 --- /dev/null +++ b/src/Umbraco.Web/Models/BackOfficeTourFilter.cs @@ -0,0 +1,61 @@ +using System.Text.RegularExpressions; + +namespace Umbraco.Web.Models +{ + public class BackOfficeTourFilter + { + public Regex PluginName { get; private set; } + public Regex TourFileName { get; private set; } + public Regex TourAlias { get; private set; } + + /// + /// Create a filter to filter out a whole plugin's tours + /// + /// + /// + public static BackOfficeTourFilter FilterPlugin(Regex pluginName) + { + return new BackOfficeTourFilter(pluginName, null, null); + } + + /// + /// Create a filter to filter out a whole tour file + /// + /// + /// + public static BackOfficeTourFilter FilterFile(Regex tourFileName) + { + return new BackOfficeTourFilter(null, tourFileName, null); + } + + /// + /// Create a filter to filter out a tour alias, this will filter out the same alias found in all files + /// + /// + /// + public static BackOfficeTourFilter FilterAlias(Regex tourAlias) + { + return new BackOfficeTourFilter(null, null, tourAlias); + } + + /// + /// Constructor to create a tour filter + /// + /// Value to filter out tours by a plugin, can be null + /// Value to filter out a tour file, can be null + /// Value to filter out a tour alias, can be null + /// + /// Depending on what is null will depend on how the filter is applied. + /// If pluginName is not NULL and it's matched then we check if tourFileName is not NULL and it's matched then we check tour alias is not NULL and then match it, + /// if any steps is NULL then the filters upstream are applied. + /// Example, pluginName = "hello", tourFileName="stuff", tourAlias=NULL = we will filter out the tour file "stuff" from the plugin "hello" but not from other plugins if the same file name exists. + /// Example, tourAlias="test.*" = we will filter out all tour aliases that start with the word "test" regardless of the plugin or file name + /// + public BackOfficeTourFilter(Regex pluginName, Regex tourFileName, Regex tourAlias) + { + PluginName = pluginName; + TourFileName = tourFileName; + TourAlias = tourAlias; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/TourFilterResolver.cs b/src/Umbraco.Web/TourFilterResolver.cs index d30c6b6093..586d0cb89f 100644 --- a/src/Umbraco.Web/TourFilterResolver.cs +++ b/src/Umbraco.Web/TourFilterResolver.cs @@ -1,32 +1,79 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Logging; +using Umbraco.Core.ObjectResolution; +using Umbraco.Web.Models; namespace Umbraco.Web { - public class TourFilterResolver + /// + /// Allows for adding filters for tours during startup + /// + public class TourFilterResolver : ManyObjectsResolverBase { - private static TourFilterResolver _current; - - private readonly HashSet _disabledTours; - - public TourFilterResolver() + public TourFilterResolver(IServiceProvider serviceProvider, ILogger logger) : base(serviceProvider, logger) { - _disabledTours = new HashSet(); } - public static TourFilterResolver Current + private readonly HashSet _instances = new HashSet(); + + public IEnumerable Filters { - get { return _current ?? (_current = new TourFilterResolver()); } + get { return Values; } } - public void Disable(string tour) + /// + /// Adds a filter instance + /// + /// + public void AddFilter(BackOfficeTourFilter filter) { - _disabledTours.Add(tour); + using (Resolution.Configuration) + _instances.Add(filter); } - public string[] DisabledTours + /// + /// Removes a filter instance + /// + /// + public void RemoveFilter(BackOfficeTourFilter filter) { - get { return _disabledTours.ToArray(); } + using (Resolution.Configuration) + _instances.Remove(filter); + } + + /// + /// Removes a filter instance based on callback + /// + /// + public void RemoveFilterWhere(Func filter) + { + using (Resolution.Configuration) + _instances.RemoveWhere(new Predicate(filter)); + } + + /// + /// + /// Overridden to return the combined created instances based on the resolved Types and the Concrete values added with AddFilter + /// + /// + protected override IEnumerable CreateInstances() + { + var createdInstances = base.CreateInstances(); + return createdInstances.Concat(_instances); + } + + public override void Clear() + { + base.Clear(); + _instances.Clear(); + } + + internal override void ResetCollections() + { + base.ResetCollections(); + _instances.Clear(); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8046e10ff8..d2c3918edf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -328,6 +328,7 @@ + diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index c38540b6a2..03934992bb 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -353,6 +353,8 @@ namespace Umbraco.Web { base.InitializeResolvers(); + TourFilterResolver.Current = new TourFilterResolver(ServiceProvider, LoggerResolver.Current.Logger); + SearchableTreeResolver.Current = new SearchableTreeResolver(ServiceProvider, LoggerResolver.Current.Logger, ApplicationContext.Services.ApplicationTreeService, () => PluginManager.ResolveSearchableTrees()); XsltExtensionsResolver.Current = new XsltExtensionsResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolveXsltExtensions());