Merge remote-tracking branch 'origin/v8/dev' into netcore/dev

Signed-off-by: Bjarke Berg <mail@bergmania.dk>

# Conflicts:
#	src/Umbraco.Infrastructure/Services/Implement/IIconService.cs
#	src/Umbraco.Infrastructure/Services/Implement/IconService.cs
#	src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml
#	src/Umbraco.Web/Editors/BackOfficeController.cs
#	src/Umbraco.Web/Editors/BackOfficeModel.cs
#	src/Umbraco.Web/Editors/BackOfficePreviewModel.cs
#	src/Umbraco.Web/Editors/IconController.cs
#	src/Umbraco.Web/Editors/PreviewController.cs
#	src/Umbraco.Web/Runtime/WebInitialComposer.cs
This commit is contained in:
Bjarke Berg
2020-09-22 12:28:56 +02:00
17 changed files with 230 additions and 125 deletions

View File

@@ -1,4 +1,4 @@
namespace Umbraco.Web.Models
namespace Umbraco.Core.Models
{
public class IconModel
{

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
namespace Umbraco.Core.Services
{
public interface IIconService
{
/// <summary>
/// Gets an IconModel containing the icon name and SvgString according to an icon name found at the global icons path
/// </summary>
/// <param name="iconName"></param>
/// <returns></returns>
IconModel GetIcon(string iconName);
/// <summary>
/// Gets a list of all svg icons found at at the global icons path.
/// </summary>
/// <returns></returns>
IList<IconModel> GetAllIcons();
}
}

View File

@@ -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<classes.length;x++) {
var cur = classes[x];
@@ -312,11 +303,17 @@ function iconHelper($http, $q, $sce, $timeout, umbRequestHelper) {
return deferred.promise;
},
/** A simple cache to ensure that the icon is only requested from the server if is isn't already in memory */
_cacheIcon: function(icon) {
if(_.find(iconCache, {name: icon.name}) === undefined) {
iconCache = _.union(iconCache, [icon]);
}
/** Creates a icon object, and caches it in a runtime cache */
defineIcon: function(name, svg) {
var icon = iconCache.find(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 */

View File

@@ -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;
}
});

View File

@@ -26,7 +26,7 @@
<div class="umb-control-group" ng-if="model.target">
<div ng-if="model.disableFocalPoint && model.target.thumbnail">
<div ng-if="vm.cropSet() === false">
<h5>
<localize key="general_preview">Preview</localize>
</h5>
@@ -34,7 +34,7 @@
<img ng-src="{{model.target.thumbnail}}" alt="{{model.target.name}}" />
</div>
<div ng-if="!model.disableFocalPoint">
<div ng-if="vm.cropSet()">
<h5>
<localize key="@general_cropSection">Crop section</localize>
</h5>

View File

@@ -21,7 +21,7 @@
<!-- Renders when it's only possible to create one specific document type for which a blueprint exits-->
<div class="btn-group" ng-show="createAllowedButtonSingleWithBlueprints" deep-blur="leaveDropdown()">
<button type="button" class="btn btn-white dropdown-toggle" aria-expanded="{{page.createDropdownOpen}}" ng-click="toggleDropdown()">
<button type="button" class="btn btn-outline umb-outline dropdown-toggle" aria-expanded="{{page.createDropdownOpen}}" ng-click="toggleDropdown()">
<span>
<localize key="actions_create">Create</localize> {{listViewAllowedTypes[0].name}}
</span>
@@ -45,7 +45,7 @@
<!-- Renders when it's possible to create multiple document types and blueprints for one or more of the document types-->
<div class="btn-group" ng-show="createAllowedButtonMultiWithBlueprints" deep-blur="leaveDropdown()">
<button type="button" class="btn btn-white dropdown-toggle" aria-expanded="{{page.createDropdownOpen === undefined ? false : page.createDropdownOpen}}" ng-click="toggleDropdown()">
<button type="button" class="btn btn-outline umb-outline dropdown-toggle" aria-expanded="{{page.createDropdownOpen === undefined ? false : page.createDropdownOpen}}" ng-click="toggleDropdown()">
<localize key="actions_create">Create</localize>
<span class="caret" aria-hidden="true"></span>
</button>

View File

@@ -68,14 +68,15 @@
</div>
<umb-tour ng-if="tour.show"
model="tour">
</umb-tour>
<umb-notifications></umb-notifications>
</div>
<umb-tour
ng-if="tour.show"
model="tour">
</umb-tour>
<!-- help dialog controller by the help button - this also forces the backoffice UI to shift 400px -->
<umb-drawer data-element="drawer" ng-if="drawer.show" model="drawer.model" view="drawer.view"></umb-drawer>
@@ -112,6 +113,12 @@
@Html.AngularValueResetPasswordCodeInfoScript(ViewData["PasswordResetCode"])
@Html.AngularValueTinyMceAssets(Current.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(Model.IconCheckData)');
iconHelper.defineIcon("icon-delete", '@Html.Raw(Model.IconDeleteData)');
}]);
//required for the noscript trick
document.getElementById("mainwrapper").style.display = "inherit";
}

View File

@@ -269,7 +269,7 @@
Defaults to "false".
@keepAlivePingUrl
The url of the KeepAlivePing action. By default, the url will use the umbracoApplicationUrl setting as the basis.
Change this setting to specify an alternative url to reach the KeepAlivePing action. eg http://localhost/api/keepalive/ping
Change this setting to specify an alternative url to reach the KeepAlivePing action. eg http://localhost/umbraco/api/keepalive/ping
Defaults to "{umbracoApplicationUrl}/api/keepalive/ping".
-->
<keepAlive disableKeepAliveTask="false" keepAlivePingUrl="{umbracoApplicationUrl}/api/keepalive/ping" />

View File

@@ -176,6 +176,9 @@ namespace Umbraco.Web.Composing
public static ISectionService SectionService
=> Factory.GetInstance<ISectionService>();
public static IIconService IconService
=> Factory.GetInstance<IIconService>();
#endregion
#region Web Constants

View File

@@ -14,6 +14,7 @@ using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Web.Models;
@@ -56,6 +57,7 @@ namespace Umbraco.Web.Editors
private readonly IRuntimeSettings _runtimeSettings;
private readonly ISecuritySettings _securitySettings;
private readonly IRuntimeMinifier _runtimeMinifier;
private readonly IIconService _iconService;
public BackOfficeController(
UmbracoFeatures features,
@@ -73,7 +75,8 @@ namespace Umbraco.Web.Editors
IHttpContextAccessor httpContextAccessor,
IRuntimeSettings settings,
ISecuritySettings securitySettings,
IRuntimeMinifier runtimeMinifier)
IRuntimeMinifier runtimeMinifier,
IIconService iconService)
: base(globalSettings, umbracoContextAccessor, services, appCaches, profilingLogger)
{
@@ -88,6 +91,7 @@ namespace Umbraco.Web.Editors
_runtimeSettings = settings;
_securitySettings = securitySettings;
_runtimeMinifier = runtimeMinifier;
_iconService = iconService;
}
protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager());
@@ -102,11 +106,12 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
public async Task<ActionResult> Default()
{
var backofficeModel = new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings, _iconService);
return await RenderDefaultOrProcessExternalLoginAsync(
() =>
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", backofficeModel),
() =>
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings))
View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/Default.cshtml", backofficeModel)
);
}
@@ -184,7 +189,7 @@ namespace Umbraco.Web.Editors
{
return await RenderDefaultOrProcessExternalLoginAsync(
//The default view to render when there is no external login info or errors
() => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
() => View(GlobalSettings.GetBackOfficePath(_hostingEnvironment).EnsureEndsWith('/') + "Views/AuthorizeUpgrade.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings, _iconService)),
//The ActionResult to perform if external login is successful
() => Redirect("/"));
}

View File

@@ -1,8 +1,9 @@
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.Features;
using Umbraco.Web.Services;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
@@ -13,7 +14,7 @@ namespace Umbraco.Web.Editors
public BackOfficeModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion,
IContentSettings contentSettings, TreeCollection treeCollection,
IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment,
IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings)
IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings, IIconService iconService)
{
Features = features;
GlobalSettings = globalSettings;
@@ -25,6 +26,8 @@ namespace Umbraco.Web.Editors
RuntimeSettings = runtimeSettings;
SecuritySettings = securitySettings;
BackOfficePath = GlobalSettings.GetBackOfficePath(HostingEnvironment);
IconCheckData = iconService.GetIcon("icon-check")?.SvgString;
IconDeleteData = iconService.GetIcon("icon-delete")?.SvgString;
}
public UmbracoFeatures Features { get; }
@@ -37,6 +40,8 @@ namespace Umbraco.Web.Editors
public IRuntimeSettings RuntimeSettings { get; set; }
public ISecuritySettings SecuritySettings { get; set; }
public string BackOfficePath { get; }
public string BackOfficePath { get; }
public string IconCheckData { get; }
public string IconDeleteData { get; }
}
}

View File

@@ -2,9 +2,10 @@
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Features;
using Umbraco.Web.Services;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
@@ -14,8 +15,29 @@ namespace Umbraco.Web.Editors
private readonly UmbracoFeatures _features;
public IEnumerable<ILanguage> Languages { get; }
public BackOfficePreviewModel(UmbracoFeatures features, IGlobalSettings globalSettings, IUmbracoVersion umbracoVersion, IEnumerable<ILanguage> languages, IContentSettings contentSettings, TreeCollection treeCollection, IHttpContextAccessor httpContextAccessor, IHostingEnvironment hostingEnvironment, IRuntimeSettings runtimeSettings, ISecuritySettings securitySettings)
: base(features, globalSettings, umbracoVersion, contentSettings, treeCollection, httpContextAccessor, hostingEnvironment, runtimeSettings, securitySettings)
public BackOfficePreviewModel(
UmbracoFeatures features,
IGlobalSettings globalSettings,
IUmbracoVersion umbracoVersion,
IEnumerable<ILanguage> languages,
IContentSettings contentSettings,
TreeCollection treeCollection,
IHttpContextAccessor httpContextAccessor,
IHostingEnvironment hostingEnvironment,
IRuntimeSettings runtimeSettings,
ISecuritySettings securitySettings,
IIconService iconService)
: base(
features,
globalSettings,
umbracoVersion,
contentSettings,
treeCollection,
httpContextAccessor,
hostingEnvironment,
runtimeSettings,
securitySettings,
iconService)
{
_features = features;
Languages = languages;

View File

@@ -1,18 +1,12 @@
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.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Mapping;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Web.Routing;
@@ -22,7 +16,7 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class IconController : UmbracoAuthorizedApiController
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IIconService _iconService;
public IconController(
IGlobalSettings globalSettings,
@@ -34,9 +28,9 @@ namespace Umbraco.Web.Editors
IRuntimeState runtimeState,
UmbracoMapper umbracoMapper,
IPublishedUrlProvider publishedUrlProvider,
IHostingEnvironment hostingEnvironment) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider)
IIconService iconService) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider)
{
_hostingEnvironment = hostingEnvironment;
_iconService = iconService;
}
/// <summary>
@@ -46,76 +40,16 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
public IconModel GetIcon(string iconName)
{
return string.IsNullOrWhiteSpace(iconName)
? null
: CreateIconModel(iconName.StripFileExtension(), _hostingEnvironment.MapPath($"{GlobalSettings.IconsPath}/{iconName}.svg"));
}
/// <summary>
/// Gets an IconModel using values from a FileInfo model
/// </summary>
/// <param name="fileInfo"></param>
/// <returns></returns>
public IconModel GetIcon(FileInfo fileInfo)
{
return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name)
? null
: CreateIconModel(fileInfo.Name.StripFileExtension(), fileInfo.FullName);
return _iconService.GetIcon(iconName);
}
/// <summary>
/// Gets a list of all svg icons found at at the global icons path.
/// </summary>
/// <returns></returns>
public List<IconModel> GetAllIcons()
public IList<IconModel> GetAllIcons()
{
var icons = new List<IconModel>();
var directory = new DirectoryInfo(_hostingEnvironment.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;
}
/// <summary>
/// Gets an IconModel containing the icon name and SvgString
/// </summary>
/// <param name="iconName"></param>
/// <param name="iconPath"></param>
/// <returns></returns>
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();
}
}
}

View File

@@ -13,6 +13,7 @@ using Umbraco.Web.Composing;
using Umbraco.Web.Features;
using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Services;
using Umbraco.Web.Trees;
using Umbraco.Web.WebAssets;
using Constants = Umbraco.Core.Constants;
@@ -27,6 +28,7 @@ namespace Umbraco.Web.Editors
private readonly IPublishedSnapshotService _publishedSnapshotService;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly ILocalizationService _localizationService;
private readonly IIconService _iconService;
private readonly IUmbracoVersion _umbracoVersion;
private readonly IContentSettings _contentSettings;
private readonly TreeCollection _treeCollection;
@@ -51,7 +53,8 @@ namespace Umbraco.Web.Editors
ICookieManager cookieManager,
IRuntimeSettings settings,
ISecuritySettings securitySettings,
IRuntimeMinifier runtimeMinifier)
IRuntimeMinifier runtimeMinifier,
IIconService iconService)
{
_features = features;
_globalSettings = globalSettings;
@@ -67,6 +70,7 @@ namespace Umbraco.Web.Editors
_runtimeSettings = settings;
_securitySettings = securitySettings;
_runtimeMinifier = runtimeMinifier;
_iconService = iconService;
}
[UmbracoAuthorize(redirectToUmbracoLogin: true)]
@@ -75,7 +79,7 @@ namespace Umbraco.Web.Editors
{
var availableLanguages = _localizationService.GetAllLanguages();
var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings);
var model = new BackOfficePreviewModel(_features, _globalSettings, _umbracoVersion, availableLanguages, _contentSettings, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings, _iconService);
if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false)
{

View File

@@ -28,6 +28,7 @@ using Umbraco.Web.AspNet;
using Umbraco.Core.Diagnostics;
using Umbraco.Core.Logging;
using Umbraco.Web.Logging;
using Umbraco.Web.Services;
namespace Umbraco.Web.Runtime
{
@@ -139,6 +140,8 @@ namespace Umbraco.Web.Runtime
composition.Register<ISessionIdResolver>(factory => factory.GetInstance<AspNetSessionManager>(), Lifetime.Singleton);
composition.Register<ISessionManager>(factory => factory.GetInstance<AspNetSessionManager>(), Lifetime.Singleton);
composition.RegisterUnique<IIconService, IconService>();
}
}
}

View File

@@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Ganss.XSS;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using File = System.IO.File;
namespace Umbraco.Web.Services
{
public class IconService : IIconService
{
private readonly IGlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
public IconService(IGlobalSettings globalSettings, IHostingEnvironment hostingEnvironment)
{
_globalSettings = globalSettings;
_hostingEnvironment = hostingEnvironment;
}
/// <inheritdoc />
public IList<IconModel> GetAllIcons()
{
var icons = new List<IconModel>();
var directory = new DirectoryInfo(_hostingEnvironment.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;
}
/// <inheritdoc />
public IconModel GetIcon(string iconName)
{
return string.IsNullOrWhiteSpace(iconName)
? null
: CreateIconModel(iconName.StripFileExtension(), _hostingEnvironment.MapPath($"{_globalSettings.IconsPath}/{iconName}.svg"));
}
/// <summary>
/// Gets an IconModel using values from a FileInfo model
/// </summary>
/// <param name="fileInfo"></param>
/// <returns></returns>
private IconModel GetIcon(FileInfo fileInfo)
{
return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name)
? null
: CreateIconModel(fileInfo.Name.StripFileExtension(), fileInfo.FullName);
}
/// <summary>
/// Gets an IconModel containing the icon name and SvgString
/// </summary>
/// <param name="iconName"></param>
/// <param name="iconPath"></param>
/// <returns></returns>
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;
}
}
}
}

View File

@@ -156,6 +156,7 @@
<Compile Include="PropertyEditors\Validation\ContentPropertyValidationResult.cs" />
<Compile Include="PropertyEditors\Validation\ValidationResultConverter.cs" />
<Compile Include="Security\IdentityFactoryMiddleware.cs" />
<Compile Include="Services\IconService.cs" />
<Compile Include="WebApi\Filters\OnlyLocalRequestsAttribute.cs" />
<Compile Include="WebAssets\CDF\ClientDependencyRuntimeMinifier.cs" />
<Compile Include="Models\NoNodesViewModel.cs" />
@@ -195,7 +196,6 @@
<Compile Include="Models\Mapping\MediaMapDefinition.cs" />
<Compile Include="Models\Mapping\MemberMapDefinition.cs" />
<Compile Include="Models\Membership\UmbracoMembershipMember.cs" />
<Compile Include="Models\IconModel.cs" />
<Compile Include="Mvc\HttpUmbracoFormRouteStringException.cs" />
<Compile Include="Mvc\ModelBindingExceptionFilter.cs" />
<Compile Include="Mvc\StatusCodeFilterAttribute.cs" />