Files
Umbraco-CMS/src/Umbraco.Web.BackOffice/Services/IconService.cs
Matt Brailsford 555b4fadf1 Lookup plugin lang / icons folders in a case insensitive way (#11985)
* Use lowercase lang folder name for packages lang files

Use lowercase lang folder name for packages lang files to be consistent with Umbraco's casing

* Case insensitive `lang` folder lookup

* Comment grammar

* Setup lower case appl_plugin icons folder path

* Update Constants-SystemDirectories.cs

* Check both casings for the AppPluginIcons path

* Fixed spelling mistake + avoid multiple Exists checks

* Add IsCaseSensitiveFileSystem to IHostingEnvironment

* Add IsCaseSensitiveFileSystem to AspNetCoreHostingEnvironment

* Undo last changes

* Undo last changed

* Add FileSystemUtility

* Only perform second iconPath if OS is case sensitive

* Undo changes

* Undo changes

* Remove filesystem utils file

* Added HostingEnvironmentExtensions.cs

* Use IsCaseSensitiveFileSystem extension method

* Use the Umbraco.Extensions namespace

* Simplify IsCaseSensitiveFileSystem

* Better naming

* Use PluginIcons

* Remove unused using statement

* Delete HostingEnvironmentExtensions.cs

* Update IconService.cs

* Comment clarity

* Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs

Co-authored-by: Paul Johnson <pmj+github@ologn.co.uk>

Co-authored-by: Paul Johnson <pmj+github@ologn.co.uk>
Co-authored-by: Michael Latouche <michael@crossingpaths.be>
2022-02-17 00:04:41 +01:00

152 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;
namespace Umbraco.Cms.Web.BackOffice.Services
{
public class IconService : IIconService
{
private readonly IOptions<GlobalSettings> _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IAppPolicyCache _cache;
public IconService(
IOptions<GlobalSettings> globalSettings,
IHostingEnvironment hostingEnvironment,
AppCaches appCaches)
{
_globalSettings = globalSettings;
_hostingEnvironment = hostingEnvironment;
_cache = appCaches.RuntimeCache;
}
/// <inheritdoc />
public IReadOnlyDictionary<string, string> GetIcons() => GetIconDictionary();
/// <inheritdoc />
public IconModel GetIcon(string iconName)
{
if (iconName.IsNullOrWhiteSpace())
{
return null;
}
var allIconModels = GetIconDictionary();
if (allIconModels.ContainsKey(iconName))
{
return new IconModel
{
Name = iconName,
SvgString = allIconModels[iconName]
};
}
return null;
}
/// <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)
{
try
{
var svgContent = System.IO.File.ReadAllText(iconPath);
var svg = new IconModel
{
Name = iconName,
SvgString = svgContent
};
return svg;
}
catch
{
return null;
}
}
private IEnumerable<FileInfo> GetAllIconsFiles()
{
var icons = new HashSet<FileInfo>(new CaseInsensitiveFileInfoComparer());
// add icons from plugins
var appPluginsDirectoryPath = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.AppPlugins);
if (Directory.Exists(appPluginsDirectoryPath))
{
var appPlugins = new DirectoryInfo(appPluginsDirectoryPath);
// iterate sub directories of app plugins
foreach (var dir in appPlugins.EnumerateDirectories())
{
// AppPluginIcons path was previoulsy the wrong case, so we first check for the prefered directory
// and then check the legacy directory.
var iconPath = _hostingEnvironment.MapPathContentRoot($"{Constants.SystemDirectories.AppPlugins}/{dir.Name}{Constants.SystemDirectories.PluginIcons}");
var iconPathExists = Directory.Exists(iconPath);
if (!iconPathExists)
{
iconPath = _hostingEnvironment.MapPathContentRoot($"{Constants.SystemDirectories.AppPlugins}/{dir.Name}{Constants.SystemDirectories.AppPluginIcons}");
iconPathExists = Directory.Exists(iconPath);
}
if (iconPathExists)
{
var dirIcons = new DirectoryInfo(iconPath).EnumerateFiles("*.svg", SearchOption.TopDirectoryOnly);
icons.UnionWith(dirIcons);
}
}
}
// add icons from IconsPath if not already added from plugins
var coreIconsDirectory = new DirectoryInfo(_hostingEnvironment.MapPathWebRoot($"{_globalSettings.Value.IconsPath}/"));
var coreIcons = coreIconsDirectory.GetFiles("*.svg");
icons.UnionWith(coreIcons);
return icons;
}
private class CaseInsensitiveFileInfoComparer : IEqualityComparer<FileInfo>
{
public bool Equals(FileInfo one, FileInfo two) => StringComparer.InvariantCultureIgnoreCase.Equals(one.Name, two.Name);
public int GetHashCode(FileInfo item) => StringComparer.InvariantCultureIgnoreCase.GetHashCode(item.Name);
}
private IReadOnlyDictionary<string, string> GetIconDictionary() => _cache.GetCacheItem(
$"{typeof(IconService).FullName}.{nameof(GetIconDictionary)}",
() => GetAllIconsFiles()
.Select(GetIcon)
.Where(i => i != null)
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First().SvgString, StringComparer.OrdinalIgnoreCase)
);
}
}