diff --git a/DNN Platform/Website/admin/Skins/Logo.ascx.cs b/DNN Platform/Website/admin/Skins/Logo.ascx.cs index 59467fda11b..903019cd83a 100644 --- a/DNN Platform/Website/admin/Skins/Logo.ascx.cs +++ b/DNN Platform/Website/admin/Skins/Logo.ascx.cs @@ -1,37 +1,46 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information namespace DotNetNuke.UI.Skins.Controls { using System; + using System.Linq; using System.Web.UI.WebControls; + using System.Xml; + using System.Xml.Linq; using DotNetNuke.Abstractions; using DotNetNuke.Common; using DotNetNuke.Common.Utilities; - using DotNetNuke.Entities.Host; using DotNetNuke.Services.Exceptions; using DotNetNuke.Services.FileSystem; + using Microsoft.Extensions.DependencyInjection; - /// ----------------------------------------------------------------------------- - /// - /// - /// - /// ----------------------------------------------------------------------------- + /// Logo Skin Object public partial class Logo : SkinObjectBase { - private readonly INavigationManager _navigationManager; + private readonly INavigationManager navigationManager; + /// Initializes a new instance of the class. public Logo() { - this._navigationManager = Globals.DependencyProvider.GetRequiredService(); + this.navigationManager = Globals.DependencyProvider.GetRequiredService(); } + /// Gets or sets the width of the border around the image. public string BorderWidth { get; set; } + /// Gets or sets the CSS class for the image. public string CssClass { get; set; } + /// Gets or sets the CSS class for the hyperlink. + public string LinkCssClass { get; set; } + + /// Gets or sets a value indicating whether to inject the SVG content inline instead of wrapping it in an img tag. + public bool InjectSvg { get; set; } + + /// protected override void OnLoad(EventArgs e) { base.OnLoad(e); @@ -47,33 +56,43 @@ protected override void OnLoad(EventArgs e) this.imgLogo.CssClass = this.CssClass; } - bool logoVisible = false; + if (!string.IsNullOrEmpty(this.LinkCssClass)) + { + this.hypLogo.CssClass = this.LinkCssClass; + } + + this.litLogo.Visible = false; + this.imgLogo.Visible = false; if (!string.IsNullOrEmpty(this.PortalSettings.LogoFile)) { var fileInfo = this.GetLogoFileInfo(); if (fileInfo != null) { - string imageUrl = FileManager.Instance.GetUrl(fileInfo); - if (!string.IsNullOrEmpty(imageUrl)) + if (this.InjectSvg && "svg".Equals(fileInfo.Extension, StringComparison.OrdinalIgnoreCase)) + { + this.litLogo.Text = this.GetSvgContent(fileInfo); + this.litLogo.Visible = !string.IsNullOrEmpty(this.litLogo.Text); + } + + if (this.litLogo.Visible == false) { - this.imgLogo.ImageUrl = imageUrl; - logoVisible = true; + string imageUrl = FileManager.Instance.GetUrl(fileInfo); + if (!string.IsNullOrEmpty(imageUrl)) + { + this.imgLogo.ImageUrl = imageUrl; + this.imgLogo.Visible = true; + } } } } - this.imgLogo.Visible = logoVisible; this.imgLogo.AlternateText = this.PortalSettings.PortalName; this.hypLogo.ToolTip = this.PortalSettings.PortalName; - - if (!this.imgLogo.Visible) - { - this.hypLogo.Attributes.Add("aria-label", this.PortalSettings.PortalName); - } + this.hypLogo.Attributes.Add("aria-label", this.PortalSettings.PortalName); if (this.PortalSettings.HomeTabId != -1) { - this.hypLogo.NavigateUrl = this._navigationManager.NavigateURL(this.PortalSettings.HomeTabId); + this.hypLogo.NavigateUrl = this.navigationManager.NavigateURL(this.PortalSettings.HomeTabId); } else { @@ -100,5 +119,61 @@ private IFileInfo GetLogoFileInfoCallBack(CacheItemArgs itemArgs) { return FileManager.Instance.GetFile(this.PortalSettings.PortalId, this.PortalSettings.LogoFile); } + + private string GetSvgContent(IFileInfo svgFile) + { + var cacheKey = string.Format(DataCache.PortalCacheKey, this.PortalSettings.PortalId, this.PortalSettings.CultureCode) + "LogoSvg"; + return CBO.GetCachedObject( + new CacheItemArgs(cacheKey, DataCache.PortalCacheTimeOut, DataCache.PortalCachePriority, svgFile), + (_) => + { + try + { + XDocument svgDocument; + using (var fileContent = FileManager.Instance.GetFileContent(svgFile)) + { + svgDocument = XDocument.Load(fileContent); + } + + var svgXmlNode = svgDocument.Descendants() + .SingleOrDefault(x => x.Name.LocalName.Equals("svg", StringComparison.Ordinal)); + if (svgXmlNode == null) + { + throw new InvalidFileContentException("The svg file has no svg node."); + } + + if (!string.IsNullOrEmpty(this.CssClass)) + { + // Append the css class. + var classes = svgXmlNode.Attribute("class")?.Value; + svgXmlNode.SetAttributeValue("class", string.IsNullOrEmpty(classes) ? this.CssClass : $"{classes} {this.CssClass}"); + } + + if (svgXmlNode.Descendants().FirstOrDefault(x => x.Name.LocalName.Equals("title", StringComparison.Ordinal)) == null) + { + // Add the title for ADA compliance. + var ns = svgXmlNode.GetDefaultNamespace(); + var titleNode = new XElement( + ns + "title", + new XAttribute("id", this.litLogo.ClientID), + this.PortalSettings.PortalName); + + svgXmlNode.AddFirst(titleNode); + + // Link the title to the svg node. + svgXmlNode.SetAttributeValue("aria-labelledby", this.litLogo.ClientID); + } + + // Ensure we have the image role for ADA Compliance + svgXmlNode.SetAttributeValue("role", "img"); + + return svgDocument.ToString(); + } + catch (XmlException ex) + { + throw new InvalidFileContentException("Invalid SVG file: " + ex.Message); + } + }); + } } } diff --git a/DNN Platform/Website/admin/Skins/Logo.ascx.designer.cs b/DNN Platform/Website/admin/Skins/Logo.ascx.designer.cs index 3e66ce34888..6b21e7d604b 100644 --- a/DNN Platform/Website/admin/Skins/Logo.ascx.designer.cs +++ b/DNN Platform/Website/admin/Skins/Logo.ascx.designer.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -11,11 +7,13 @@ // //------------------------------------------------------------------------------ -namespace DotNetNuke.UI.Skins.Controls { - - - public partial class Logo { - +namespace DotNetNuke.UI.Skins.Controls +{ + + + public partial class Logo + { + /// /// hypLogo control. /// @@ -24,7 +22,7 @@ public partial class Logo { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.HyperLink hypLogo; - + /// /// imgLogo control. /// @@ -33,5 +31,14 @@ public partial class Logo { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.Image imgLogo; + + /// + /// litLogo control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal litLogo; } } diff --git a/DNN Platform/Website/admin/Skins/Logo.xml b/DNN Platform/Website/admin/Skins/Logo.xml index 1d134a29973..5efd91598a1 100644 --- a/DNN Platform/Website/admin/Skins/Logo.xml +++ b/DNN Platform/Website/admin/Skins/Logo.xml @@ -5,4 +5,24 @@ Sets the border width of the logo image + + CssClass + String + Sets a CSS class on the logo + + + + LinkCssClass + String + Sets a CSS class on the hyperlink + + + + InjectSvg + Boolean + + If set to true, the control will read the contents of the Logo file, look for an svg tag, and inject it directly into the hyperlink + + + diff --git a/DNN Platform/Website/admin/Skins/logo.ascx b/DNN Platform/Website/admin/Skins/logo.ascx index 2922fd4d030..f90ddf01c9b 100644 --- a/DNN Platform/Website/admin/Skins/logo.ascx +++ b/DNN Platform/Website/admin/Skins/logo.ascx @@ -1,2 +1,5 @@ -<%@ Control Language="C#" AutoEventWireup="false" Inherits="DotNetNuke.UI.Skins.Controls.Logo" ViewStateMode="Disabled" Codebehind="Logo.ascx.cs" %> - \ No newline at end of file +<%@ Control Language="C#" AutoEventWireup="false" Inherits="DotNetNuke.UI.Skins.Controls.Logo" ViewStateMode="Disabled" CodeBehind="Logo.ascx.cs" %> + \ No newline at end of file