diff --git a/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs b/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs index 10d12f30f8..40d145afb0 100644 --- a/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs +++ b/src/Umbraco.Core/Editors/BackOfficePreviewModel.cs @@ -1,7 +1,4 @@ using System.Collections.Generic; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Hosting; using Umbraco.Core.Models; using Umbraco.Web.Features; @@ -10,7 +7,7 @@ namespace Umbraco.Web.Editors public class BackOfficePreviewModel { private readonly UmbracoFeatures _features; - + public BackOfficePreviewModel(UmbracoFeatures features, IEnumerable languages) { _features = features; diff --git a/src/Umbraco.Core/Models/IconModel.cs b/src/Umbraco.Core/Models/IconModel.cs index 5c79ad6219..12fa8884ae 100644 --- a/src/Umbraco.Core/Models/IconModel.cs +++ b/src/Umbraco.Core/Models/IconModel.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Web.Models +namespace Umbraco.Core.Models { public class IconModel { diff --git a/src/Umbraco.Core/Services/IIconService.cs b/src/Umbraco.Core/Services/IIconService.cs new file mode 100644 index 0000000000..9ae9164c72 --- /dev/null +++ b/src/Umbraco.Core/Services/IIconService.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Web.Models; + +namespace Umbraco.Core.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.BackOffice/Controllers/IconController.cs b/src/Umbraco.Web.BackOffice/Controllers/IconController.cs index 83d6ba299d..28509e2425 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/IconController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/IconController.cs @@ -1,31 +1,19 @@ using System.Collections.Generic; -using System.Linq; -using Umbraco.Web.Models; -using System.IO; -using Umbraco.Core; -using Ganss.XSS; -using Microsoft.Extensions.Options; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.Models; -using Umbraco.Core.Hosting; +using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.Common.Attributes; -using Umbraco.Web.Common.Filters; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] public class IconController : UmbracoAuthorizedApiController { - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IOptions _globalSettings; + private readonly IIconService _iconService; - public IconController( - IHostingEnvironment hostingEnvironment, - IOptions globalSettings) + public IconController(IIconService iconService) { - _hostingEnvironment = hostingEnvironment; - _globalSettings = globalSettings; + _iconService = iconService; } /// @@ -36,78 +24,16 @@ namespace Umbraco.Web.Editors [DetermineAmbiguousActionByPassingParameters] public IconModel GetIcon(string iconName) { - return string.IsNullOrWhiteSpace(iconName) - ? null - : CreateIconModel(iconName.StripFileExtension(), - _hostingEnvironment.MapPathWebRoot($"{_globalSettings.Value.IconsPath}/{iconName}.svg")); - } - - /// - /// Gets an IconModel using values from a FileInfo model - /// - /// - /// - [DetermineAmbiguousActionByPassingParameters] - 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(_hostingEnvironment.MapPathWebRoot($"{_globalSettings.Value.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 = System.IO.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.BackOffice/Controllers/PreviewController.cs b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs index 771ca28f55..fb6d597d9e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs @@ -21,6 +21,8 @@ using Umbraco.Web.Editors; using Umbraco.Web.Features; using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; +using Umbraco.Web.Services; +using Umbraco.Web.Trees; using Umbraco.Web.WebAssets; using Constants = Umbraco.Core.Constants; diff --git a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs index 648fa6b031..fdf7c3bbd6 100644 --- a/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs +++ b/src/Umbraco.Web.BackOffice/Runtime/BackOfficeComposer.cs @@ -4,10 +4,12 @@ using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Hosting; using Umbraco.Core.IO; +using Umbraco.Core.Services; using Umbraco.Extensions; using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Routing; using Umbraco.Web.BackOffice.Security; +using Umbraco.Web.BackOffice.Services; using Umbraco.Web.BackOffice.Trees; using Umbraco.Web.Common.Runtime; using Umbraco.Web.Trees; @@ -45,6 +47,8 @@ namespace Umbraco.Web.BackOffice.Runtime factory.GetInstance(), factory.GetInstance>(), "~/")); + + composition.RegisterUnique(); } } } diff --git a/src/Umbraco.Web.BackOffice/Services/IconService.cs b/src/Umbraco.Web.BackOffice/Services/IconService.cs new file mode 100644 index 0000000000..84bd98a459 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Services/IconService.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Ganss.XSS; +using Microsoft.Extensions.Options; +using Umbraco.Core; +using Umbraco.Core.Configuration.Models; +using Umbraco.Core.Hosting; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using File = System.IO.File; + +namespace Umbraco.Web.BackOffice.Services +{ + public class IconService : IIconService + { + private readonly IOptions _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; + + public IconService(IOptions globalSettings, IHostingEnvironment hostingEnvironment) + { + _globalSettings = globalSettings; + _hostingEnvironment = hostingEnvironment; + } + + + /// + public IList GetAllIcons() + { + var icons = new List(); + var directory = new DirectoryInfo(_hostingEnvironment.MapPathWebRoot($"{_globalSettings.Value.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(), _hostingEnvironment.MapPathWebRoot($"{_globalSettings.Value.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.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs index 58b7fc16b5..45411b5e69 100644 --- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs +++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Hosting; using Umbraco.Core.Logging; using Umbraco.Core.Runtime; using Umbraco.Core.Security; +using Umbraco.Core.Services; using Umbraco.Extensions; using Umbraco.Net; using Umbraco.Web.Common.AspNetCore; @@ -99,8 +100,6 @@ namespace Umbraco.Web.Common.Runtime composition.RegisterUnique(); composition.RegisterUnique(); composition.RegisterUnique(factory => new LegacyPasswordSecurity(factory.GetInstance>().Value)); - - } } } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js index 0d0135fff8..5034de67eb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/iconhelper.service.js @@ -31,7 +31,7 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { { oldIcon: ".sprToPublish", newIcon: "mail-forward" }, { oldIcon: ".sprTranslate", newIcon: "comments" }, { oldIcon: ".sprUpdate", newIcon: "save" }, - + { oldIcon: ".sprTreeSettingDomain", newIcon: "icon-home" }, { oldIcon: ".sprTreeDoc", newIcon: "icon-document" }, { oldIcon: ".sprTreeDoc2", newIcon: "icon-diploma-alt" }, @@ -39,21 +39,21 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { { oldIcon: ".sprTreeDoc4", newIcon: "icon-newspaper-alt" }, { oldIcon: ".sprTreeDoc5", newIcon: "icon-notepad-alt" }, - { oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" }, + { oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" }, { oldIcon: ".sprTreeFolder", newIcon: "icon-folder" }, { oldIcon: ".sprTreeFolder_o", newIcon: "icon-folder" }, { oldIcon: ".sprTreeMediaFile", newIcon: "icon-music" }, { oldIcon: ".sprTreeMediaMovie", newIcon: "icon-movie" }, { oldIcon: ".sprTreeMediaPhoto", newIcon: "icon-picture" }, - + { oldIcon: ".sprTreeMember", newIcon: "icon-user" }, { oldIcon: ".sprTreeMemberGroup", newIcon: "icon-users" }, { oldIcon: ".sprTreeMemberType", newIcon: "icon-users" }, - + { oldIcon: ".sprTreeNewsletter", newIcon: "icon-file-text-alt" }, { oldIcon: ".sprTreePackage", newIcon: "icon-box" }, { oldIcon: ".sprTreeRepository", newIcon: "icon-server-alt" }, - + { oldIcon: ".sprTreeSettingDataType", newIcon: "icon-autofill" }, // TODO: Something needs to be done with the old tree icons that are commented out. @@ -61,7 +61,7 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { { oldIcon: ".sprTreeSettingAgent", newIcon: "" }, { oldIcon: ".sprTreeSettingCss", newIcon: "" }, { oldIcon: ".sprTreeSettingCssItem", newIcon: "" }, - + { oldIcon: ".sprTreeSettingDataTypeChild", newIcon: "" }, { oldIcon: ".sprTreeSettingDomain", newIcon: "" }, { oldIcon: ".sprTreeSettingLanguage", newIcon: "" }, @@ -94,9 +94,9 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { var iconCache = []; var liveRequests = []; var allIconsRequested = false; - + return { - + /** Used by the create dialogs for content/media types to format the data so that the thumbnails are styled properly */ formatContentTypeThumbnails: function (contentTypes) { for (var i = 0; i < contentTypes.length; i++) { @@ -209,15 +209,11 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { ,'Failed to retrieve icon: ' + iconName) .then(icon => { if(icon) { - var trustedIcon = { - name: icon.Name, - svgString: $sce.trustAsHtml(icon.SvgString) - }; - this._cacheIcon(trustedIcon); + var trustedIcon = this.defineIcon(icon.Name, icon.SvgString); - liveRequests = _.filter(liveRequests, iconRequestPath); + liveRequests = _.filter(liveRequests, iconRequestPath); - resolve(trustedIcon); + resolve(trustedIcon); } }) .catch(err => { @@ -240,15 +236,10 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { ,'Failed to retrieve icons') .then(icons => { icons.forEach(icon => { - var trustedIcon = { - name: icon.Name, - svgString: $sce.trustAsHtml(icon.SvgString) - }; - - this._cacheIcon(trustedIcon); + this.defineIcon(icon.Name, icon.SvgString); }); - resolve(iconCache); + resolve(iconCache); }) .catch(err => { console.warn(err); @@ -278,7 +269,7 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) { console.warn("Can't read the css rules of: " + document.styleSheets[i].href, e); continue; } - + if (classes !== null) { for(var x=0;x x.name === name); + if(icon === undefined) { + icon = { + name: name, + svgString: $sce.trustAsHtml(svg) + }; + iconCache.push(icon); + } + return icon; }, /** Returns the cached icon or undefined */ diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.controller.js index d9ea5a7a09..030200e1e6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.controller.js @@ -6,6 +6,7 @@ vm.submit = submit; vm.close = close; + vm.cropSet = cropSet; if (!$scope.model.target.coordinates && !$scope.model.target.focalPoint) { $scope.model.target.focalPoint = { left: .5, top: .5 }; @@ -56,4 +57,8 @@ } } + function cropSet() { + var model = $scope.model; + return (model.cropSize || {}).width && model.target.thumbnail; + } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.html index 3814ac851e..cda42043c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.html @@ -26,7 +26,7 @@
-
+
Preview
@@ -34,7 +34,7 @@ {{model.target.name}}
-
+
Crop section
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 05a294cc1c..e72b81b019 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -21,7 +21,7 @@
- diff --git a/src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml b/src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml index 41cd42b037..221f18b92d 100644 --- a/src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml +++ b/src/Umbraco.Web.UI.NetCore/umbraco/UmbracoBackOffice/Default.cshtml @@ -8,6 +8,7 @@ @using Umbraco.Core.Hosting @using Umbraco.Extensions @using Umbraco.Core.Logging +@using Umbraco.Core.Services @using Umbraco.Web.BackOffice.Controllers @inject BackOfficeSignInManager signInManager @inject BackOfficeServerVariables backOfficeServerVariables @@ -16,7 +17,7 @@ @inject IOptions globalSettings @inject IRuntimeMinifier runtimeMinifier @inject IProfilerHtml profilerHtml - +@inject IIconService IconService @{ bool.TryParse(Context.Request.Query["umbDebug"], out bool isDebug); var backOfficePath = globalSettings.Value.GetBackOfficePath(hostingEnvironment); @@ -111,6 +112,13 @@ @await Html.AngularValueExternalLoginInfoScriptAsync(signInManager, ViewData.GetExternalSignInError()) @Html.AngularValueResetPasswordCodeInfoScript(ViewData["PasswordResetCode"]) @await Html.AngularValueTinyMceAssetsAsync(runtimeMinifier) + + app.run(["iconHelper", function (iconHelper) { + @* We inject icons to the icon helper(service), since icons can only be loaded if user is authorized. By injecting these to the service they will not be requested as they will become cached. *@ + iconHelper.defineIcon("icon-check", '@Html.Raw(IconService.GetIcon("icon-check")?.SvgString)'); + iconHelper.defineIcon("icon-delete", '@Html.Raw(IconService.GetIcon("icon-delete")?.SvgString)'); + }]); + //required for the noscript trick document.getElementById("mainwrapper").style.display = "inherit"; } diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index a940cd3657..64a716b605 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -178,6 +178,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 65746b5367..f8848f64f0 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -11,6 +11,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; +using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Web.Mvc; using Umbraco.Core.Services; @@ -38,7 +39,8 @@ namespace Umbraco.Web.Editors private readonly IHostingEnvironment _hostingEnvironment; private readonly RuntimeSettings _runtimeSettings; private readonly SecuritySettings _securitySettings; - private readonly Microsoft.Extensions.Logging.ILogger _logger; + private readonly IIconService _iconService; + private readonly ILogger _logger; public BackOfficeController( UmbracoFeatures features, @@ -52,7 +54,8 @@ namespace Umbraco.Web.Editors IOptions contentSettings, IHostingEnvironment hostingEnvironment, IOptions settings, - IOptions securitySettings) + IOptions securitySettings, + IIconService iconService) : base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger, loggerFactory) { @@ -62,6 +65,7 @@ namespace Umbraco.Web.Editors _hostingEnvironment = hostingEnvironment; _runtimeSettings = settings.Value; _securitySettings = securitySettings.Value; + _iconService = iconService; _logger = loggerFactory.CreateLogger(); } diff --git a/src/Umbraco.Web/Models/IconModel.cs b/src/Umbraco.Web/Models/IconModel.cs deleted file mode 100644 index 5c79ad6219..0000000000 --- a/src/Umbraco.Web/Models/IconModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Umbraco.Web.Models -{ - public class IconModel - { - public string Name { get; set; } - public string SvgString { get; set; } - } -} diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index ad6e1b830b..f6629ad5fd 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Web.Security; +using System.Web.Security; using Microsoft.AspNet.SignalR; using Umbraco.Core; using Umbraco.Core.Composing; @@ -15,8 +14,7 @@ using Umbraco.Web.PublishedCache; using Umbraco.Web.Security; using Umbraco.Web.Security.Providers; using Umbraco.Web.SignalR; -using Umbraco.Web.Templates; -using Umbraco.Web.Trees; +using Umbraco.Web.Services; namespace Umbraco.Web.Runtime { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0b675f61a4..c11f4bd09c 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -172,7 +172,6 @@ - @@ -407,6 +406,8 @@ Mvc\web.config - + + + \ No newline at end of file