diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 2419eaa6d4..14898d0c02 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -146,6 +146,9 @@ namespace Umbraco.Web.Composing public static ISectionService SectionService => Factory.GetInstance(); + public static IIconService IconService + => Factory.GetInstance(); + #endregion #region Web Constants diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index e77a1b70f2..c9106cffb5 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -26,6 +26,7 @@ using Umbraco.Web.Composing; using Umbraco.Web.Features; using Umbraco.Web.JavaScript; using Umbraco.Web.Security; +using Umbraco.Web.Services; using Constants = Umbraco.Core.Constants; using JArray = Newtonsoft.Json.Linq.JArray; @@ -42,15 +43,54 @@ namespace Umbraco.Web.Editors private readonly ManifestParser _manifestParser; private readonly UmbracoFeatures _features; private readonly IRuntimeState _runtimeState; + private readonly IIconService _iconService; private BackOfficeUserManager _userManager; private BackOfficeSignInManager _signInManager; - public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) + [Obsolete("Use the constructor that injects IIconService.")] + public BackOfficeController( + ManifestParser manifestParser, + UmbracoFeatures features, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IRuntimeState runtimeState, + UmbracoHelper umbracoHelper) + : this(manifestParser, + features, + globalSettings, + umbracoContextAccessor, + services, + appCaches, + profilingLogger, + runtimeState, + umbracoHelper, + Current.IconService) + { + _manifestParser = manifestParser; + _features = features; + _runtimeState = runtimeState; + } + + public BackOfficeController( + ManifestParser manifestParser, + UmbracoFeatures features, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ServiceContext services, + AppCaches appCaches, + IProfilingLogger profilingLogger, + IRuntimeState runtimeState, + UmbracoHelper umbracoHelper, + IIconService iconService) : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger, umbracoHelper) { _manifestParser = manifestParser; _features = features; _runtimeState = runtimeState; + _iconService = iconService; } protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager()); @@ -66,8 +106,8 @@ namespace Umbraco.Web.Editors public async Task Default() { return await RenderDefaultOrProcessExternalLoginAsync( - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings)), - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings))); + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _iconService)), + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _iconService))); } [HttpGet] @@ -150,7 +190,7 @@ namespace Umbraco.Web.Editors { return await RenderDefaultOrProcessExternalLoginAsync( //The default view to render when there is no external login info or errors - () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings)), + () => View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _iconService)), //The ActionResult to perform if external login is successful () => Redirect("/")); } diff --git a/src/Umbraco.Web/Editors/BackOfficeModel.cs b/src/Umbraco.Web/Editors/BackOfficeModel.cs index b11ea6a5d1..b620d893e3 100644 --- a/src/Umbraco.Web/Editors/BackOfficeModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficeModel.cs @@ -1,21 +1,29 @@ -using Umbraco.Core.Configuration; +using System; +using Umbraco.Core.Configuration; +using Umbraco.Web.Composing; using Umbraco.Web.Features; +using Umbraco.Web.Services; namespace Umbraco.Web.Editors { public class BackOfficeModel { - private IconController IconController { get; } - public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings) + + + [Obsolete("Use the overload that injects IIconService.")] + public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings) : this(features, globalSettings, Current.IconService) + { + + } + public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IIconService iconService) { Features = features; GlobalSettings = globalSettings; - IconController = new IconController(); - IconCheckData = IconController.GetIcon("icon-check")?.SvgString; - IconDeleteData = IconController.GetIcon("icon-delete")?.SvgString; + IconCheckData = iconService.GetIcon("icon-check")?.SvgString; + IconDeleteData = iconService.GetIcon("icon-delete")?.SvgString; } - + public UmbracoFeatures Features { get; } public IGlobalSettings GlobalSettings { get; } public string IconCheckData { get; } diff --git a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs index b66e432699..7239bba8f7 100644 --- a/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Web/Editors/BackOfficePreviewModel.cs @@ -1,7 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; + using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Web.Composing; using Umbraco.Web.Features; +using Umbraco.Web.Services; namespace Umbraco.Web.Editors { @@ -10,7 +14,21 @@ namespace Umbraco.Web.Editors private readonly UmbracoFeatures _features; public IEnumerable Languages { get; } - public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IEnumerable languages) : base(features, globalSettings) + [Obsolete("Use the overload that injects IIconService.")] + public BackOfficePreviewModel( + UmbracoFeatures features, + IGlobalSettings globalSettings, + IEnumerable languages) + : this(features, globalSettings, languages, Current.IconService) + { + } + + public BackOfficePreviewModel( + UmbracoFeatures features, + IGlobalSettings globalSettings, + IEnumerable languages, + IIconService iconService) + : base(features, globalSettings, iconService) { _features = features; Languages = languages; diff --git a/src/Umbraco.Web/Editors/IconController.cs b/src/Umbraco.Web/Editors/IconController.cs index 72af2194ae..b45b0948c8 100644 --- a/src/Umbraco.Web/Editors/IconController.cs +++ b/src/Umbraco.Web/Editors/IconController.cs @@ -1,21 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; -using Umbraco.Core.Logging; using Umbraco.Web.Models; -using System.IO; -using Umbraco.Core; -using Umbraco.Core.IO; -using Ganss.XSS; -using Umbraco.Core.Cache; +using Umbraco.Web.Services; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] public class IconController : UmbracoAuthorizedApiController { + private readonly IIconService _iconService; + + public IconController(IIconService iconService) + { + _iconService = iconService; + } /// /// Gets an IconModel containing the icon name and SvgString according to an icon name found at the global icons path @@ -24,76 +23,16 @@ namespace Umbraco.Web.Editors /// public IconModel GetIcon(string iconName) { - return string.IsNullOrWhiteSpace(iconName) - ? null - : CreateIconModel(iconName.StripFileExtension(), IOHelper.MapPath($"{GlobalSettings.IconsPath}/{iconName}.svg")); - } - - /// - /// Gets an IconModel using values from a FileInfo model - /// - /// - /// - public IconModel GetIcon(FileInfo fileInfo) - { - return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name) - ? null - : CreateIconModel(fileInfo.Name.StripFileExtension(), fileInfo.FullName); + return _iconService.GetIcon(iconName); } /// /// Gets a list of all svg icons found at at the global icons path. /// /// - public List GetAllIcons() + public IList GetAllIcons() { - var icons = new List(); - var directory = new DirectoryInfo(IOHelper.MapPath($"{GlobalSettings.IconsPath}/")); - var iconNames = directory.GetFiles("*.svg"); - - iconNames.OrderBy(f => f.Name).ToList().ForEach(iconInfo => - { - var icon = GetIcon(iconInfo); - - if (icon != null) - { - icons.Add(icon); - } - }); - - return icons; - } - - /// - /// Gets an IconModel containing the icon name and SvgString - /// - /// - /// - /// - private IconModel CreateIconModel(string iconName, string iconPath) - { - var sanitizer = new HtmlSanitizer(); - sanitizer.AllowedAttributes.UnionWith(Core.Constants.SvgSanitizer.Attributes); - sanitizer.AllowedCssProperties.UnionWith(Core.Constants.SvgSanitizer.Attributes); - sanitizer.AllowedTags.UnionWith(Core.Constants.SvgSanitizer.Tags); - - try - { - var svgContent = File.ReadAllText(iconPath); - var sanitizedString = sanitizer.Sanitize(svgContent); - - var svg = new IconModel - { - Name = iconName, - SvgString = sanitizedString - }; - - return svg; - } - catch - { - return null; - } + return _iconService.GetAllIcons(); } } } diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index c1848419dc..f148655acd 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -12,6 +12,7 @@ using Umbraco.Web.JavaScript; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Services; using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors @@ -24,19 +25,39 @@ namespace Umbraco.Web.Editors private readonly IPublishedSnapshotService _publishedSnapshotService; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILocalizationService _localizationService; + private readonly IIconService _iconService; + [Obsolete("Use the constructor that injects IIconService.")] public PreviewController( UmbracoFeatures features, IGlobalSettings globalSettings, IPublishedSnapshotService publishedSnapshotService, IUmbracoContextAccessor umbracoContextAccessor, ILocalizationService localizationService) + :this(features, + globalSettings, + publishedSnapshotService, + umbracoContextAccessor, + localizationService, + Current.IconService) + { + + } + + public PreviewController( + UmbracoFeatures features, + IGlobalSettings globalSettings, + IPublishedSnapshotService publishedSnapshotService, + IUmbracoContextAccessor umbracoContextAccessor, + ILocalizationService localizationService, + IIconService iconService) { _features = features; _globalSettings = globalSettings; _publishedSnapshotService = publishedSnapshotService; _umbracoContextAccessor = umbracoContextAccessor; _localizationService = localizationService; + _iconService = iconService; } [UmbracoAuthorize(redirectToUmbracoLogin: true)] @@ -45,7 +66,7 @@ namespace Umbraco.Web.Editors { var availableLanguages = _localizationService.GetAllLanguages(); - var model = new BackOfficePreviewModel(_features, _globalSettings, availableLanguages); + var model = new BackOfficePreviewModel(_features, _globalSettings, availableLanguages, _iconService); if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false) { diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 135d54560b..d9a8ee37f2 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -139,6 +139,7 @@ namespace Umbraco.Web.Runtime composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(factory => ExamineManager.Instance); diff --git a/src/Umbraco.Web/Services/IIconService.cs b/src/Umbraco.Web/Services/IIconService.cs new file mode 100644 index 0000000000..177921ceae --- /dev/null +++ b/src/Umbraco.Web/Services/IIconService.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Umbraco.Web.Models; + +namespace Umbraco.Web.Services +{ + public interface IIconService + { + /// + /// Gets an IconModel containing the icon name and SvgString according to an icon name found at the global icons path + /// + /// + /// + IconModel GetIcon(string iconName); + + /// + /// Gets a list of all svg icons found at at the global icons path. + /// + /// + IList GetAllIcons(); + } +} diff --git a/src/Umbraco.Web/Services/IconService.cs b/src/Umbraco.Web/Services/IconService.cs new file mode 100644 index 0000000000..93389668b6 --- /dev/null +++ b/src/Umbraco.Web/Services/IconService.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ganss.XSS; +using Umbraco.Core; +using Umbraco.Core.Configuration; +using Umbraco.Core.IO; +using Umbraco.Web.Models; + +namespace Umbraco.Web.Services +{ + public class IconService : IIconService + { + private readonly IGlobalSettings _globalSettings; + + public IconService(IGlobalSettings globalSettings) + { + _globalSettings = globalSettings; + } + + + /// + public IList GetAllIcons() + { + var icons = new List(); + var directory = new DirectoryInfo(IOHelper.MapPath($"{_globalSettings.IconsPath}/")); + var iconNames = directory.GetFiles("*.svg"); + + iconNames.OrderBy(f => f.Name).ToList().ForEach(iconInfo => + { + var icon = GetIcon(iconInfo); + + if (icon != null) + { + icons.Add(icon); + } + }); + + return icons; + } + + /// + public IconModel GetIcon(string iconName) + { + return string.IsNullOrWhiteSpace(iconName) + ? null + : CreateIconModel(iconName.StripFileExtension(), IOHelper.MapPath($"{_globalSettings.IconsPath}/{iconName}.svg")); + } + + /// + /// Gets an IconModel using values from a FileInfo model + /// + /// + /// + private IconModel GetIcon(FileInfo fileInfo) + { + return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name) + ? null + : CreateIconModel(fileInfo.Name.StripFileExtension(), fileInfo.FullName); + } + + /// + /// Gets an IconModel containing the icon name and SvgString + /// + /// + /// + /// + private IconModel CreateIconModel(string iconName, string iconPath) + { + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedAttributes.UnionWith(Core.Constants.SvgSanitizer.Attributes); + sanitizer.AllowedCssProperties.UnionWith(Core.Constants.SvgSanitizer.Attributes); + sanitizer.AllowedTags.UnionWith(Core.Constants.SvgSanitizer.Tags); + + try + { + var svgContent = File.ReadAllText(iconPath); + var sanitizedString = sanitizer.Sanitize(svgContent); + + var svg = new IconModel + { + Name = iconName, + SvgString = sanitizedString + }; + + return svg; + } + catch + { + return null; + } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 33051671ee..ff67493aaa 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -238,8 +238,8 @@ - + @@ -284,10 +284,12 @@ + +