Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V11: Using IFileProvider to access assets added from packages #13141

Merged
merged 28 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c5d1452
Creating a FileProviderFactory for getting the package.manifest and g…
elit0451 Oct 7, 2022
09babdc
Collecting the package.manifest-s from different sources
elit0451 Oct 7, 2022
2cc30b5
Searching different sources for grid.editors.config.js
elit0451 Oct 7, 2022
12b756b
Using an IFileProvider to collect all tours
elit0451 Oct 7, 2022
9c3804e
Refactoring IconService.cs
elit0451 Oct 7, 2022
14b2d22
Typo
elit0451 Oct 7, 2022
e67ce45
Optimizations when looping through the file system
elit0451 Oct 7, 2022
8bdfc24
Moving WebRootFileProviderFactory to Umbraco.Web.Common proj
elit0451 Oct 10, 2022
cc69c27
Removes double registering
elit0451 Oct 10, 2022
4b62c59
Merge branch 'v11/dev' into v11/feature/use-IFileProvider
elit0451 Oct 13, 2022
712810e
pluginLangFileSources includes the localPluginFileSources
elit0451 Oct 14, 2022
e342d98
Merge branch 'v11/dev' into v11/feature/use-IFileProvider
elit0451 Oct 17, 2022
3dfd2b7
Comments
elit0451 Oct 17, 2022
3b8d556
Merge branch 'v11/dev' into v11/feature/use-IFileProvider
elit0451 Oct 19, 2022
1b969a9
Remove linq from foreach
elit0451 Oct 19, 2022
32a22ea
Merge branch 'v11/dev' into v11/feature/use-IFileProvider
elit0451 Oct 19, 2022
bfeb80d
Merge branch 'v11/dev' into v11/feature/use-IFileProvider
elit0451 Oct 27, 2022
3fc3a79
Change workflow for grid.editors.config.js so we check first physical…
elit0451 Oct 27, 2022
fe41071
Clean up
elit0451 Oct 27, 2022
156bf70
Check if config dir exists
elit0451 Oct 31, 2022
df841cf
Discover nested package.manifest files
elit0451 Nov 1, 2022
a8cb1c6
Fix IFileInfo.PhysicalPath check
elit0451 Nov 1, 2022
b2eb78b
Merge remote-tracking branch 'origin/v11/dev' into v11/feature/use-IF…
elit0451 Nov 1, 2022
43baf3e
Revert 712810e1fd995720047832ee689f804185ea69d6 as that way files in …
elit0451 Nov 1, 2022
34f9103
Adding comments
elit0451 Nov 2, 2022
9038ca8
Refactoring
elit0451 Nov 2, 2022
b84967c
Remove PhysicalPath check
elit0451 Nov 2, 2022
8e4c7a6
Fix registration of WebRootFileProviderFactory
elit0451 Nov 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/Umbraco.Core/Configuration/Grid/GridConfig.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Web.Common.DependencyInjection;

namespace Umbraco.Cms.Core.Configuration.Grid;

Expand All @@ -13,9 +16,27 @@ public GridConfig(
IManifestParser manifestParser,
IJsonSerializer jsonSerializer,
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory)
=> EditorsConfig =
new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger<GridEditorsConfig>());
new GridEditorsConfig(appCaches, hostingEnvironment, manifestParser, jsonSerializer, loggerFactory.CreateLogger<GridEditorsConfig>(), gridEditorsConfigFileProviderFactory);

[Obsolete("Use other ctor - Will be removed in Umbraco 13")]
public GridConfig(
AppCaches appCaches,
IManifestParser manifestParser,
IJsonSerializer jsonSerializer,
IHostingEnvironment hostingEnvironment,
ILoggerFactory loggerFactory)
: this(
appCaches,
manifestParser,
jsonSerializer,
hostingEnvironment,
loggerFactory,
StaticServiceProvider.Instance.GetRequiredService<IGridEditorsConfigFileProviderFactory>())
{
}

public IGridEditorsConfig EditorsConfig { get; }
}
86 changes: 68 additions & 18 deletions src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using System.Reflection;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;

namespace Umbraco.Cms.Core.Configuration.Grid;

Expand All @@ -17,20 +20,40 @@ internal class GridEditorsConfig : IGridEditorsConfig

private readonly IJsonSerializer _jsonSerializer;
private readonly ILogger<GridEditorsConfig> _logger;
private readonly IGridEditorsConfigFileProviderFactory _gridEditorsConfigFileProviderFactory;
private readonly IManifestParser _manifestParser;

public GridEditorsConfig(
AppCaches appCaches,
IHostingEnvironment hostingEnvironment,
IManifestParser manifestParser,
IJsonSerializer jsonSerializer,
ILogger<GridEditorsConfig> logger)
ILogger<GridEditorsConfig> logger,
IGridEditorsConfigFileProviderFactory gridEditorsConfigFileProviderFactory)
{
_appCaches = appCaches;
_hostingEnvironment = hostingEnvironment;
_manifestParser = manifestParser;
_jsonSerializer = jsonSerializer;
_logger = logger;
_gridEditorsConfigFileProviderFactory = gridEditorsConfigFileProviderFactory;
}

[Obsolete("Use other ctor - Will be removed in Umbraco 13")]
public GridEditorsConfig(
AppCaches appCaches,
IHostingEnvironment hostingEnvironment,
IManifestParser manifestParser,
IJsonSerializer jsonSerializer,
ILogger<GridEditorsConfig> logger)
: this(
appCaches,
hostingEnvironment,
manifestParser,
jsonSerializer,
logger,
StaticServiceProvider.Instance.GetRequiredService<IGridEditorsConfigFileProviderFactory>())
{
}

public IEnumerable<IGridEditorConfig> Editors
Expand All @@ -39,13 +62,37 @@ public IEnumerable<IGridEditorConfig> Editors
{
List<IGridEditorConfig> GetResult()
{
var configFolder =
new DirectoryInfo(_hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config));
IFileInfo? gridConfig = null;
var editors = new List<IGridEditorConfig>();
var gridConfig = Path.Combine(configFolder.FullName, "grid.editors.config.js");
if (File.Exists(gridConfig))
var configPath = Constants.SystemDirectories.Config.TrimStart(Constants.CharArrays.Tilde);

// Get physical file if it exists
var configPhysicalDirPath = _hostingEnvironment.MapPathContentRoot(configPath);

if (Directory.Exists(configPhysicalDirPath) == true)
{
var physicalFileProvider = new PhysicalFileProvider(configPhysicalDirPath);
gridConfig = GetConfigFile(physicalFileProvider, string.Empty);
}

// If there is no physical file, check in RCLs
if (gridConfig is null)
{
IFileProvider? compositeFileProvider = _gridEditorsConfigFileProviderFactory.Create();

if (compositeFileProvider is null)
{
throw new ArgumentNullException(nameof(compositeFileProvider));
}

gridConfig = GetConfigFile(compositeFileProvider, configPath);
}

if (gridConfig is not null)
{
var sourceString = File.ReadAllText(gridConfig);
using Stream stream = gridConfig.CreateReadStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
var sourceString = reader.ReadToEnd();

try
{
Expand All @@ -63,19 +110,16 @@ List<IGridEditorConfig> GetResult()
// Read default from embedded file
else
{
Assembly assembly = GetType().Assembly;
Stream? resourceStream = assembly.GetManifestResourceStream(
"Umbraco.Cms.Core.EmbeddedResources.Grid.grid.editors.config.js");
IFileProvider configFileProvider = new EmbeddedFileProvider(GetType().Assembly, "Umbraco.Cms.Core.EmbeddedResources.Grid");
IFileInfo embeddedConfig = configFileProvider.GetFileInfo("grid.editors.config.js");

if (resourceStream is not null)
{
using var reader = new StreamReader(resourceStream, Encoding.UTF8);
var sourceString = reader.ReadToEnd();
editors.AddRange(_jsonSerializer.Deserialize<IEnumerable<GridEditor>>(sourceString)!);
}
using Stream stream = embeddedConfig.CreateReadStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
var sourceString = reader.ReadToEnd();
editors.AddRange(_jsonSerializer.Deserialize<IEnumerable<GridEditor>>(sourceString)!);
}

// add manifest editors, skip duplicates
// Add manifest editors, skip duplicates
foreach (GridEditor gridEditor in _manifestParser.CombinedManifest.GridEditors)
{
if (editors.Contains(gridEditor) == false)
Expand All @@ -95,4 +139,10 @@ List<IGridEditorConfig> GetResult()
return result!;
}
}

private static IFileInfo? GetConfigFile(IFileProvider fileProvider, string path)
{
IFileInfo fileInfo = fileProvider.GetFileInfo($"{path}/grid.editors.config.js");
return fileInfo.Exists ? fileInfo : null;
}
}
10 changes: 10 additions & 0 deletions src/Umbraco.Core/IO/IGridEditorsConfigFileProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.Extensions.FileProviders;

namespace Umbraco.Cms.Core.IO;

/// <summary>
/// Factory for creating <see cref="IFileProvider" /> instances for providing the grid.editors.config.js file.
/// </summary>
public interface IGridEditorsConfigFileProviderFactory : IFileProviderFactory
{
}
10 changes: 10 additions & 0 deletions src/Umbraco.Core/IO/IManifestFileProviderFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.Extensions.FileProviders;

namespace Umbraco.Cms.Core.IO;

/// <summary>
/// Factory for creating <see cref="IFileProvider" /> instances for providing the package.manifest file.
/// </summary>
public interface IManifestFileProviderFactory : IFileProviderFactory
{
}
92 changes: 82 additions & 10 deletions src/Umbraco.Infrastructure/Manifest/ManifestParser.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;

namespace Umbraco.Cms.Core.Manifest;
Expand All @@ -21,6 +25,7 @@ public class ManifestParser : IManifestParser

private readonly IAppPolicyCache _cache;
private readonly IDataValueEditorFactory _dataValueEditorFactory;
private readonly IManifestFileProviderFactory _manifestFileProviderFactory;
private readonly ManifestFilterCollection _filters;
private readonly IHostingEnvironment _hostingEnvironment;

Expand All @@ -46,7 +51,8 @@ public ManifestParser(
IJsonSerializer jsonSerializer,
ILocalizedTextService localizedTextService,
IShortStringHelper shortStringHelper,
IDataValueEditorFactory dataValueEditorFactory)
IDataValueEditorFactory dataValueEditorFactory,
IManifestFileProviderFactory manifestFileProviderFactory)
{
if (appCaches == null)
{
Expand All @@ -64,6 +70,34 @@ public ManifestParser(
_localizedTextService = localizedTextService;
_shortStringHelper = shortStringHelper;
_dataValueEditorFactory = dataValueEditorFactory;
_manifestFileProviderFactory = manifestFileProviderFactory;
}

[Obsolete("Use other ctor - Will be removed in Umbraco 13")]
public ManifestParser(
AppCaches appCaches,
ManifestValueValidatorCollection validators,
ManifestFilterCollection filters,
ILogger<ManifestParser> logger,
IIOHelper ioHelper,
IHostingEnvironment hostingEnvironment,
IJsonSerializer jsonSerializer,
ILocalizedTextService localizedTextService,
IShortStringHelper shortStringHelper,
IDataValueEditorFactory dataValueEditorFactory)
: this(
appCaches,
validators,
filters,
logger,
ioHelper,
hostingEnvironment,
jsonSerializer,
localizedTextService,
shortStringHelper,
dataValueEditorFactory,
StaticServiceProvider.Instance.GetRequiredService<IManifestFileProviderFactory>())
{
}

public string AppPluginsPath
Expand All @@ -89,25 +123,33 @@ public CompositePackageManifest CombinedManifest
public IEnumerable<PackageManifest> GetManifests()
{
var manifests = new List<PackageManifest>();
IFileProvider? manifestFileProvider = _manifestFileProviderFactory.Create();

if (manifestFileProvider is null)
{
throw new ArgumentNullException(nameof(manifestFileProvider));
}

foreach (var path in GetManifestFiles())
foreach (IFileInfo file in GetManifestFiles(manifestFileProvider, Constants.SystemDirectories.AppPlugins))
{
try
{
var text = File.ReadAllText(path);
using Stream stream = file.CreateReadStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
var text = reader.ReadToEnd();
text = TrimPreamble(text);
if (string.IsNullOrWhiteSpace(text))
{
continue;
}

PackageManifest manifest = ParseManifest(text);
manifest.Source = path;
manifest.Source = file.PhysicalPath!; // We assure that the PhysicalPath is not null in GetManifestFiles()
manifests.Add(manifest);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", path);
_logger.LogError(e, "Failed to parse manifest at '{Path}', ignoring.", file.PhysicalPath);
}
}

Expand Down Expand Up @@ -242,14 +284,44 @@ private static string TrimPreamble(string text)
return text;
}

// gets all manifest files (recursively)
private IEnumerable<string> GetManifestFiles()
// Gets all manifest files
private static IEnumerable<IFileInfo> GetManifestFiles(IFileProvider fileProvider, string path)
{
if (Directory.Exists(_path) == false)
var manifestFiles = new List<IFileInfo>();
IEnumerable<IFileInfo> pluginFolders = fileProvider.GetDirectoryContents(path);

foreach (IFileInfo pluginFolder in pluginFolders)
{
return Array.Empty<string>();
if (!pluginFolder.IsDirectory)
{
continue;
}

manifestFiles.AddRange(GetNestedManifestFiles(fileProvider, $"{path}/{pluginFolder.Name}"));
}

return Directory.GetFiles(_path, "package.manifest", SearchOption.AllDirectories);
return manifestFiles;
}

// Helper method to get all nested package.manifest files (recursively)
private static IEnumerable<IFileInfo> GetNestedManifestFiles(IFileProvider fileProvider, string path)
{
foreach (IFileInfo file in fileProvider.GetDirectoryContents(path))
{
if (file.IsDirectory)
{
var virtualPath = WebPath.Combine(path, file.Name);

// Recursively find nested package.manifest files
foreach (IFileInfo nested in GetNestedManifestFiles(fileProvider, virtualPath))
{
yield return nested;
}
}
else if (file.Name.InvariantEquals("package.manifest") && !string.IsNullOrEmpty(file.PhysicalPath))
{
yield return file;
}
}
}
}
Loading