V10: merge release branch 20220620 (#12590)
* Add Umbraco specific global usings * Enable implicit usings * v10: Wait for updated ConnectionStrings during install (#12536) * Do not change/reload configuration * Wait for updated connection string options * recase assigndomain (#12448) * Add depth property to ICoreScope (#12540) * Remove ambient scope stack from httpcontext.items. (#12539) This change makes it easier to use service calls in parallel whilst a httpcontext is available. * v10: Prefer SQLite primitive types to flexible types (#12541) * Prefer SQLite primitive types to flexible types. * SQLite - column mappings use TEXT for decimals Thanks @mattbrailsford for sense check. * Fix issue where languages files are not found in subdir of package dir (#12543) * Make FindContent return type nullable (#12545) * Updated nuget dependencies (07-06-2022) (#12525) * Updated nuget dependencies * Move Nerdbank.GitVersioning update to Directory.Build.props * Updated more dependencies * Improve FlagOutOfDateModels property behaviour. (cherry picked from commit 54077725c373495fce0d3fbc5cdb6469aad3b676) * Fix logic error WRT models builder flag out of date models. (#12548) (cherry picked from commit 6b0149803a879d1c6902a5f61d1f2e9dc8545aac) * Fixed issue with expected null value. (#12550) Fixes https://github.com/umbraco/Umbraco-CMS/issues/12526 * Updated Examine to 3.0.0 * Fixes relation issue, when moving a root item to recycle bin, the "Relate Parent Media Folder On Delete"/"Relate Parent Document On Delete" cannot get the parent node type, because it is a fake root. * Fix possible null error * Bump version to 10.0.0 final * Fix attempting to write lock files to LocalTempPath before it exists (#12563) * Re fix usage statements Co-authored-by: Ronald Barendse <ronald@barend.se> Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Co-authored-by: Paul Johnson <pmj@umbraco.com> Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<Version>3.5.103</Version>
|
||||
<Version>3.5.107</Version>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Umbraco.GitVersioning.Extensions" Version="0.1.1">
|
||||
|
||||
@@ -7,13 +7,16 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.6.10" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.7.2" />
|
||||
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="9.3.1" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="9.3.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="10.0.0-rc1" />
|
||||
<PackageReference Include="Umbraco.Deploy.Core" Version="9.4.0" />
|
||||
<PackageReference Include="Umbraco.Forms.Core" Version="9.4.0" />
|
||||
<PackageReference Update="Nerdbank.GitVersioning">
|
||||
<Version>3.5.107</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -38,6 +38,20 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase<SqliteSyntaxProvider>
|
||||
[typeof(Guid)] = new SqliteGuidScalarMapper(),
|
||||
[typeof(Guid?)] = new SqliteNullableGuidScalarMapper(),
|
||||
};
|
||||
|
||||
IntColumnDefinition = "INTEGER";
|
||||
LongColumnDefinition = "INTEGER";
|
||||
BoolColumnDefinition = "INTEGER";
|
||||
|
||||
GuidColumnDefinition = "TEXT";
|
||||
DateTimeColumnDefinition = "TEXT";
|
||||
DateTimeOffsetColumnDefinition = "TEXT";
|
||||
TimeColumnDefinition = "TEXT";
|
||||
DecimalColumnDefinition = "TEXT"; // REAL would be lossy. - https://docs.microsoft.com/en-us/dotnet/standard/data/sqlite/types
|
||||
|
||||
RealColumnDefinition = "REAL";
|
||||
|
||||
BlobColumnDefinition = "BLOB";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="buildTransitive\**" PackagePath="buildTransitive" />
|
||||
<Content Include="$(JsonSchemaPath)" PackagePath="."/>
|
||||
<Content Include="$(JsonSchemaPath)" PackagePath="." />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CheckPreConditions" BeforeTargets="Build">
|
||||
|
||||
@@ -5,4 +5,8 @@
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);umbraco/Logs/**</DefaultItemExcludes>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);wwwroot/media/**</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Using Include="Umbraco.Cms.Core.DependencyInjection" />
|
||||
<Using Include="Umbraco.Extensions" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -41,19 +41,20 @@ public class ModelsBuilderSettings
|
||||
/// </remarks>
|
||||
public bool FlagOutOfDateModels
|
||||
{
|
||||
get => _flagOutOfDateModels;
|
||||
|
||||
set
|
||||
get
|
||||
{
|
||||
if (!ModelsMode.IsAuto())
|
||||
if (ModelsMode == ModelsMode.Nothing ||ModelsMode.IsAuto())
|
||||
{
|
||||
_flagOutOfDateModels = false;
|
||||
return;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
_flagOutOfDateModels = value;
|
||||
return _flagOutOfDateModels;
|
||||
}
|
||||
|
||||
set =>_flagOutOfDateModels = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the models directory.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
@@ -18,7 +18,7 @@ public class RelationItem
|
||||
public string? NodeType { get; set; }
|
||||
|
||||
[DataMember(Name = "udi")]
|
||||
public Udi NodeUdi => Udi.Create(NodeType, NodeKey);
|
||||
public Udi? NodeUdi => NodeType == Constants.UdiEntityType.Unknown ? null : Udi.Create(NodeType, NodeKey);
|
||||
|
||||
[DataMember(Name = "icon")]
|
||||
public string? ContentTypeIcon { get; set; }
|
||||
|
||||
@@ -8,6 +8,14 @@ namespace Umbraco.Cms.Core.Scoping;
|
||||
/// </summary>
|
||||
public interface ICoreScope : IDisposable, IInstanceIdentifiable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the distance from the root scope.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A zero represents a root scope, any value greater than zero represents a child scope.
|
||||
/// </remarks>
|
||||
public int Depth => -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scope notification publisher
|
||||
/// </summary>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Core" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<None Remove="obj\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Examine" Version="3.0.0-beta.9" />
|
||||
<PackageReference Include="Examine" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -35,11 +35,6 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
|
||||
public void RemoveConnectionString()
|
||||
{
|
||||
// Update and reload configuration
|
||||
_configuration[UmbracoConnectionStringPath] = null;
|
||||
_configuration[UmbracoConnectionStringProviderNamePath] = null;
|
||||
(_configuration as IConfigurationRoot)?.Reload();
|
||||
|
||||
// Remove keys from JSON
|
||||
var provider = GetJsonConfigurationProvider(UmbracoConnectionStringPath);
|
||||
|
||||
@@ -58,11 +53,6 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
|
||||
public void SaveConnectionString(string connectionString, string? providerName)
|
||||
{
|
||||
// Update and reload configuration
|
||||
_configuration[UmbracoConnectionStringPath] = connectionString;
|
||||
_configuration[UmbracoConnectionStringProviderNamePath] = providerName;
|
||||
(_configuration as IConfigurationRoot)?.Reload();
|
||||
|
||||
// Save keys to JSON
|
||||
var provider = GetJsonConfigurationProvider();
|
||||
|
||||
@@ -84,10 +74,6 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
|
||||
public void SaveConfigValue(string key, object value)
|
||||
{
|
||||
// Update and reload configuration
|
||||
_configuration[key] = value?.ToString();
|
||||
(_configuration as IConfigurationRoot)?.Reload();
|
||||
|
||||
// Save key to JSON
|
||||
var provider = GetJsonConfigurationProvider();
|
||||
|
||||
@@ -122,10 +108,6 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
|
||||
public void SaveDisableRedirectUrlTracking(bool disable)
|
||||
{
|
||||
// Update and reload configuration
|
||||
_configuration["Umbraco:CMS:WebRouting:DisableRedirectUrlTracking"] = disable.ToString();
|
||||
(_configuration as IConfigurationRoot)?.Reload();
|
||||
|
||||
// Save key to JSON
|
||||
var provider = GetJsonConfigurationProvider();
|
||||
|
||||
@@ -147,10 +129,6 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
|
||||
public void SetGlobalId(string id)
|
||||
{
|
||||
// Update and reload configuration
|
||||
_configuration["Umbraco:CMS:Global:Id"] = id;
|
||||
(_configuration as IConfigurationRoot)?.Reload();
|
||||
|
||||
// Save key to JSON
|
||||
var provider = GetJsonConfigurationProvider();
|
||||
|
||||
@@ -336,17 +314,21 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
{
|
||||
if (token is JObject obj)
|
||||
{
|
||||
|
||||
foreach (var property in obj.Properties())
|
||||
{
|
||||
if (name is null)
|
||||
{
|
||||
return property.Value;
|
||||
}
|
||||
|
||||
if (string.Equals(property.Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return property.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +97,14 @@ public static partial class UmbracoBuilderExtensions
|
||||
builder.Mappers()?.AddCoreMappers();
|
||||
|
||||
// register the scope provider
|
||||
builder.Services.AddSingleton<ScopeProvider>(); // implements IScopeProvider, IScopeAccessor
|
||||
builder.Services.AddSingleton<ScopeProvider>(sp => ActivatorUtilities.CreateInstance<ScopeProvider>(sp, sp.GetRequiredService<IAmbientScopeStack>())); // implements IScopeProvider, IScopeAccessor
|
||||
builder.Services.AddSingleton<ICoreScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<Core.Scoping.IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<IScopeAccessor>(f => f.GetRequiredService<ScopeProvider>());
|
||||
|
||||
builder.Services.AddSingleton<IAmbientScopeStack, AmbientScopeStack>();
|
||||
builder.Services.AddSingleton<IScopeAccessor>(f => f.GetRequiredService<IAmbientScopeStack>());
|
||||
builder.Services.AddSingleton<IAmbientScopeContextStack, AmbientScopeContextStack>();
|
||||
|
||||
builder.Services.AddScoped<IHttpScopeReference, HttpScopeReference>();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Serilog;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Parsing;
|
||||
|
||||
@@ -27,14 +27,14 @@ public class MessageTemplates : IMessageTemplates
|
||||
throw new FormatException($"Could not format message \"{messageTemplate}\" with {args.Length} args.");
|
||||
}
|
||||
|
||||
var values = boundProperties.ToDictionary(x => x.Name, x => x.Value);
|
||||
var values = boundProperties!.ToDictionary(x => x.Name, x => x.Value);
|
||||
|
||||
// this ends up putting every string parameter between quotes
|
||||
// return parsedTemplate.Render(values);
|
||||
|
||||
// this does not
|
||||
var tw = new StringWriter();
|
||||
foreach (MessageTemplateToken? t in parsedTemplate.Tokens)
|
||||
foreach (MessageTemplateToken? t in parsedTemplate!.Tokens)
|
||||
{
|
||||
if (t is PropertyToken pt &&
|
||||
values.TryGetValue(pt.PropertyName, out LogEventPropertyValue? propVal) &&
|
||||
|
||||
@@ -163,7 +163,27 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
|
||||
if (!isTrialRun)
|
||||
{
|
||||
// File configuration providers use a delay before reloading and triggering changes, so wait
|
||||
using var isChanged = new ManualResetEvent(false);
|
||||
using IDisposable? onChange = _connectionStrings.OnChange((options, name) =>
|
||||
{
|
||||
// Only watch default named option (CurrentValue)
|
||||
if (name != Options.DefaultName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal change
|
||||
isChanged.Set();
|
||||
});
|
||||
|
||||
// Update configuration and wait for change
|
||||
_configManipulator.SaveConnectionString(connectionString, providerName);
|
||||
if (!isChanged.WaitOne(10_000))
|
||||
{
|
||||
throw new InstallException("Didn't retrieve updated connection string within 10 seconds, try manual configuration instead.");
|
||||
}
|
||||
|
||||
Configure(_globalSettings.CurrentValue.InstallMissingDatabase || providerMeta.ForceCreateDatabase);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Umbraco.Cms.Infrastructure.Runtime;
|
||||
internal class FileSystemMainDomLock : IMainDomLock
|
||||
{
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly string _lockFilePath;
|
||||
private readonly ILogger<FileSystemMainDomLock> _logger;
|
||||
private readonly string _releaseSignalFilePath;
|
||||
@@ -25,7 +26,8 @@ internal class FileSystemMainDomLock : IMainDomLock
|
||||
IOptionsMonitor<GlobalSettings> globalSettings)
|
||||
{
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_globalSettings = globalSettings;
|
||||
|
||||
var lockFileName = $"MainDom_{mainDomKeyGenerator.GenerateKey()}.lock";
|
||||
_lockFilePath = Path.Combine(hostingEnvironment.LocalTempPath, lockFileName);
|
||||
@@ -41,7 +43,7 @@ internal class FileSystemMainDomLock : IMainDomLock
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath);
|
||||
Directory.CreateDirectory(_hostingEnvironment.LocalTempPath);_logger.LogDebug("Attempting to obtain MainDom lock file handle {lockFilePath}", _lockFilePath);
|
||||
_lockFileStream = File.Open(_lockFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
|
||||
DeleteLockReleaseSignalFile();
|
||||
return Task.FromResult(true);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
internal class AmbientScopeContextStack : IAmbientScopeContextStack
|
||||
{
|
||||
private static AsyncLocal<ConcurrentStack<IScopeContext>> _stack = new();
|
||||
|
||||
public IScopeContext? AmbientContext
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stack.Value?.TryPeek(out IScopeContext? ambientContext) ?? false)
|
||||
{
|
||||
return ambientContext;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IScopeContext Pop()
|
||||
{
|
||||
if (_stack.Value?.TryPop(out IScopeContext? ambientContext) ?? false)
|
||||
{
|
||||
return ambientContext;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientContext was found.");
|
||||
}
|
||||
|
||||
public void Push(IScopeContext scope)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IScopeContext>();
|
||||
|
||||
_stack.Value.Push(scope);
|
||||
}
|
||||
}
|
||||
39
src/Umbraco.Infrastructure/Scoping/AmbientScopeStack.cs
Normal file
39
src/Umbraco.Infrastructure/Scoping/AmbientScopeStack.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
internal class AmbientScopeStack : IAmbientScopeStack
|
||||
{
|
||||
private static AsyncLocal<ConcurrentStack<IScope>> _stack = new ();
|
||||
|
||||
public IScope? AmbientScope
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_stack.Value?.TryPeek(out IScope? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IScope Pop()
|
||||
{
|
||||
if (_stack.Value?.TryPop(out IScope? ambientScope) ?? false)
|
||||
{
|
||||
return ambientScope;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("No AmbientScope was found.");
|
||||
}
|
||||
|
||||
public void Push(IScope scope)
|
||||
{
|
||||
_stack.Value ??= new ConcurrentStack<IScope>();
|
||||
|
||||
_stack.Value.Push(scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
internal interface IAmbientScopeContextStack
|
||||
{
|
||||
IScopeContext? AmbientContext { get; }
|
||||
IScopeContext Pop();
|
||||
void Push(IScopeContext scope);
|
||||
}
|
||||
7
src/Umbraco.Infrastructure/Scoping/IAmbientScopeStack.cs
Normal file
7
src/Umbraco.Infrastructure/Scoping/IAmbientScopeStack.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
internal interface IAmbientScopeStack : IScopeAccessor
|
||||
{
|
||||
IScope Pop();
|
||||
void Push(IScope scope);
|
||||
}
|
||||
@@ -33,7 +33,6 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
private readonly bool? _scopeFileSystem;
|
||||
|
||||
private readonly ScopeProvider _scopeProvider;
|
||||
private bool _callContext;
|
||||
private bool? _completed;
|
||||
private IUmbracoDatabase? _database;
|
||||
|
||||
@@ -86,7 +85,6 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_notificationPublisher = notificationPublisher;
|
||||
_scopeFileSystem = scopeFileSystems;
|
||||
_callContext = callContext;
|
||||
_autoComplete = autoComplete;
|
||||
Detachable = detachable;
|
||||
_dictionaryLocker = new object();
|
||||
@@ -248,24 +246,15 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Scopes are never stored on HttpContext.Items anymore, so CallContext is always true.")]
|
||||
// a value indicating whether to force call-context
|
||||
public bool CallContext
|
||||
{
|
||||
get
|
||||
get => true;
|
||||
set
|
||||
{
|
||||
if (_callContext)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ParentScope != null)
|
||||
{
|
||||
return ParentScope.CallContext;
|
||||
}
|
||||
|
||||
return false;
|
||||
// NOOP - always true.
|
||||
}
|
||||
set => _callContext = value;
|
||||
}
|
||||
|
||||
public bool ScopedFileSystems
|
||||
@@ -467,6 +456,19 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
}
|
||||
}
|
||||
|
||||
public int Depth
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ParentScope == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ParentScope.Depth + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public IScopedNotificationPublisher Notifications
|
||||
{
|
||||
get
|
||||
@@ -540,7 +542,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
}
|
||||
}
|
||||
|
||||
_scopeProvider.PopAmbientScope(this); // might be null = this is how scopes are removed from context objects
|
||||
_scopeProvider.PopAmbientScope(); // might be null = this is how scopes are removed from context objects
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.Disposed(this);
|
||||
@@ -885,7 +887,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
// by Deploy which I don't fully understand since there is limited tests on this in the CMS
|
||||
if (OrigScope != _scopeProvider.AmbientScope)
|
||||
{
|
||||
_scopeProvider.PopAmbientScope(_scopeProvider.AmbientScope);
|
||||
_scopeProvider.PopAmbientScope();
|
||||
}
|
||||
|
||||
if (OrigContext != _scopeProvider.AmbientContext)
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Data;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DistributedLocking;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using CoreDebugSettings = Umbraco.Cms.Core.Configuration.Models.CoreDebugSettings;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -18,26 +20,52 @@ using System.Text;
|
||||
namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IScopeProvider" />.
|
||||
/// Implements <see cref="IScopeProvider"/>.
|
||||
/// </summary>
|
||||
internal class ScopeProvider :
|
||||
ICoreScopeProvider,
|
||||
IScopeProvider,
|
||||
Core.Scoping.IScopeProvider,
|
||||
IScopeAccessor
|
||||
IScopeAccessor // TODO: No need to implement this here but literally hundreds of our tests cast ScopeProvider to ScopeAccessor
|
||||
{
|
||||
private readonly ILogger<ScopeProvider> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IRequestCache _requestCache;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IAmbientScopeStack _ambientScopeStack;
|
||||
private readonly IAmbientScopeContextStack _ambientContextStack;
|
||||
|
||||
private readonly FileSystems _fileSystems;
|
||||
private CoreDebugSettings _coreDebugSettings;
|
||||
private readonly MediaFileManager _mediaFileManager;
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScope>> _scopeStack = new();
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScopeContext>> _scopeContextStack = new();
|
||||
private static readonly string _scopeItemKey = typeof(Scope).FullName!;
|
||||
private static readonly string _contextItemKey = typeof(ScopeProvider).FullName!;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public ScopeProvider(
|
||||
IAmbientScopeStack ambientScopeStack,
|
||||
IAmbientScopeContextStack ambientContextStack,
|
||||
IDistributedLockingMechanismFactory distributedLockingMechanismFactory,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
FileSystems fileSystems,
|
||||
IOptionsMonitor<CoreDebugSettings> coreDebugSettings,
|
||||
MediaFileManager mediaFileManager,
|
||||
ILoggerFactory loggerFactory,
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
DistributedLockingMechanismFactory = distributedLockingMechanismFactory;
|
||||
DatabaseFactory = databaseFactory;
|
||||
_ambientScopeStack = ambientScopeStack;
|
||||
_ambientContextStack = ambientContextStack;
|
||||
_fileSystems = fileSystems;
|
||||
_coreDebugSettings = coreDebugSettings.CurrentValue;
|
||||
_mediaFileManager = mediaFileManager;
|
||||
_logger = loggerFactory.CreateLogger<ScopeProvider>();
|
||||
_loggerFactory = loggerFactory;
|
||||
_eventAggregator = eventAggregator;
|
||||
// take control of the FileSystems
|
||||
_fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems;
|
||||
|
||||
coreDebugSettings.OnChange(x => _coreDebugSettings = x);
|
||||
}
|
||||
|
||||
[Obsolete("Please use an alternative constructor. This constructor is due for removal in v12.")]
|
||||
public ScopeProvider(
|
||||
IDistributedLockingMechanismFactory distributedLockingMechanismFactory,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
@@ -47,21 +75,17 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
ILoggerFactory loggerFactory,
|
||||
IRequestCache requestCache,
|
||||
IEventAggregator eventAggregator)
|
||||
: this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IAmbientScopeStack>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<IAmbientScopeContextStack>(),
|
||||
distributedLockingMechanismFactory,
|
||||
databaseFactory,
|
||||
fileSystems,
|
||||
coreDebugSettings,
|
||||
mediaFileManager,
|
||||
loggerFactory,
|
||||
eventAggregator)
|
||||
{
|
||||
DistributedLockingMechanismFactory = distributedLockingMechanismFactory;
|
||||
DatabaseFactory = databaseFactory;
|
||||
_fileSystems = fileSystems;
|
||||
_coreDebugSettings = coreDebugSettings.CurrentValue;
|
||||
_mediaFileManager = mediaFileManager;
|
||||
_logger = loggerFactory.CreateLogger<ScopeProvider>();
|
||||
_loggerFactory = loggerFactory;
|
||||
_requestCache = requestCache;
|
||||
_eventAggregator = eventAggregator;
|
||||
|
||||
// take control of the FileSystems
|
||||
_fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems;
|
||||
|
||||
coreDebugSettings.OnChange(x => _coreDebugSettings = x);
|
||||
}
|
||||
|
||||
public IDistributedLockingMechanismFactory DistributedLockingMechanismFactory { get; }
|
||||
@@ -70,282 +94,32 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
|
||||
public ISqlContext SqlContext => DatabaseFactory.SqlContext;
|
||||
|
||||
#region Context
|
||||
|
||||
private void MoveHttpContextScopeToCallContext()
|
||||
{
|
||||
var source = (ConcurrentStack<IScope>?)_requestCache.Get(_scopeItemKey);
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
MoveContexts(_scopeItemKey, source, stack, (_, v) => _scopeStack.Value = v);
|
||||
}
|
||||
|
||||
private void MoveHttpContextScopeContextToCallContext()
|
||||
{
|
||||
var source = (ConcurrentStack<IScopeContext>?)_requestCache.Get(_contextItemKey);
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
MoveContexts(_contextItemKey, source, stack, (_, v) => _scopeContextStack.Value = v);
|
||||
}
|
||||
|
||||
private void MoveCallContextScopeToHttpContext()
|
||||
{
|
||||
ConcurrentStack<IScope>? source = _scopeStack.Value;
|
||||
var stack = (ConcurrentStack<IScope>?)_requestCache.Get(_scopeItemKey);
|
||||
MoveContexts(_scopeItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
}
|
||||
|
||||
private void MoveCallContextScopeContextToHttpContext()
|
||||
{
|
||||
ConcurrentStack<IScopeContext>? source = _scopeContextStack.Value;
|
||||
var stack = (ConcurrentStack<IScopeContext>?)_requestCache.Get(_contextItemKey);
|
||||
MoveContexts(_contextItemKey, source, stack, (k, v) => _requestCache.Set(k, v));
|
||||
}
|
||||
|
||||
private void MoveContexts<T>(string key, ConcurrentStack<T>? source, ConcurrentStack<T>? stack, Action<string, ConcurrentStack<T>> setter)
|
||||
where T : class, IInstanceIdentifiable
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (stack != null)
|
||||
{
|
||||
stack.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: This isn't going to copy it back up the execution context chain
|
||||
stack = new ConcurrentStack<T>();
|
||||
setter(key, stack);
|
||||
}
|
||||
|
||||
var arr = new T[source.Count];
|
||||
source.CopyTo(arr, 0);
|
||||
Array.Reverse(arr);
|
||||
foreach (T a in arr)
|
||||
{
|
||||
stack.Push(a);
|
||||
}
|
||||
|
||||
source.Clear();
|
||||
}
|
||||
|
||||
private void SetCallContextScope(IScope? value)
|
||||
{
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
// first, null-register the existing value
|
||||
if (stack != null && stack.TryPeek(out IScope ambientScope))
|
||||
{
|
||||
RegisterContext(ambientScope, null);
|
||||
}
|
||||
|
||||
// then register the new value
|
||||
if (value != null)
|
||||
{
|
||||
RegisterContext(value, "call");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (stack != null)
|
||||
{
|
||||
stack.TryPop(out _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if DEBUG_SCOPES
|
||||
_logger.LogDebug("AddObject " + value.InstanceId.ToString("N").Substring(0, 8));
|
||||
#endif
|
||||
if (stack == null)
|
||||
{
|
||||
stack = new ConcurrentStack<IScope>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
_scopeStack.Value = stack;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCallContextScopeContext(IScopeContext? value)
|
||||
{
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (stack != null)
|
||||
{
|
||||
stack.TryPop(out _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stack == null)
|
||||
{
|
||||
stack = new ConcurrentStack<IScopeContext>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
_scopeContextStack.Value = stack;
|
||||
}
|
||||
}
|
||||
|
||||
private T? GetHttpContextObject<T>(string key, bool required = true)
|
||||
where T : class
|
||||
{
|
||||
if (!_requestCache.IsAvailable && required)
|
||||
{
|
||||
throw new Exception("Request cache is unavailable.");
|
||||
}
|
||||
|
||||
var stack = (ConcurrentStack<T>?)_requestCache.Get(key);
|
||||
return stack != null && stack.TryPeek(out T? peek) ? peek : null;
|
||||
}
|
||||
|
||||
private bool SetHttpContextObject<T>(string key, T? value, bool required = true)
|
||||
{
|
||||
if (!_requestCache.IsAvailable)
|
||||
{
|
||||
if (required)
|
||||
{
|
||||
throw new Exception("Request cache is unavailable.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
// manage the 'context' that contains the scope (null, "http" or "call")
|
||||
// only for scopes of course!
|
||||
if (key == s_scopeItemKey)
|
||||
{
|
||||
// first, null-register the existing value
|
||||
var ambientScope = (IScope)_requestCache.Get(s_scopeItemKey);
|
||||
if (ambientScope != null)
|
||||
{
|
||||
RegisterContext(ambientScope, null);
|
||||
}
|
||||
|
||||
// then register the new value
|
||||
if (value is IScope scope)
|
||||
{
|
||||
RegisterContext(scope, "http");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
var stack = (ConcurrentStack<T>?)_requestCache.Get(key);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
if (stack != null)
|
||||
{
|
||||
stack.TryPop(out _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stack == null)
|
||||
{
|
||||
stack = new ConcurrentStack<T>();
|
||||
}
|
||||
|
||||
stack.Push(value);
|
||||
_requestCache.Set(key, stack);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ambient Context
|
||||
|
||||
/// <summary>
|
||||
/// Get the Ambient (Current) <see cref="IScopeContext" /> for the current execution context.
|
||||
/// Get the Ambient (Current) <see cref="IScopeContext"/> for the current execution context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// </remarks>
|
||||
public IScopeContext? AmbientContext
|
||||
{
|
||||
get
|
||||
{
|
||||
// try http context, fallback onto call context
|
||||
IScopeContext? value = GetHttpContextObject<IScopeContext>(_contextItemKey, false);
|
||||
if (value != null)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
ConcurrentStack<IScopeContext>? stack = _scopeContextStack.Value;
|
||||
if (stack == null || !stack.TryPeek(out IScopeContext? peek))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return peek;
|
||||
}
|
||||
}
|
||||
public IScopeContext? AmbientContext => _ambientContextStack.AmbientContext;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ambient Scope
|
||||
|
||||
IScope? IScopeAccessor.AmbientScope => AmbientScope;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the Ambient (Current) <see cref="Scope" /> for the current execution context.
|
||||
/// Gets or set the Ambient (Current) <see cref="Scope"/> for the current execution context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// The current execution context may be request based (HttpContext) or on a background thread (AsyncLocal)
|
||||
/// </remarks>
|
||||
public Scope? AmbientScope
|
||||
{
|
||||
get
|
||||
{
|
||||
// try http context, fallback onto call context
|
||||
IScope? value = GetHttpContextObject<IScope>(_scopeItemKey, false);
|
||||
if (value != null)
|
||||
{
|
||||
return (Scope)value;
|
||||
}
|
||||
public Scope? AmbientScope => (Scope?)_ambientScopeStack.AmbientScope;
|
||||
|
||||
ConcurrentStack<IScope>? stack = _scopeStack.Value;
|
||||
if (stack == null || !stack.TryPeek(out IScope? peek))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
IScope? IScopeAccessor.AmbientScope => _ambientScopeStack.AmbientScope;
|
||||
|
||||
return (Scope)peek;
|
||||
}
|
||||
}
|
||||
|
||||
public void PopAmbientScope(Scope? scope)
|
||||
{
|
||||
// pop the stack from all contexts
|
||||
SetHttpContextObject<IScope>(_scopeItemKey, null, false);
|
||||
SetCallContextScope(null);
|
||||
|
||||
// We need to move the stack to a different context if the parent scope
|
||||
// is flagged with a different CallContext flag. This is required
|
||||
// if creating a child scope with callContext: true (thus forcing CallContext)
|
||||
// when there is actually a current HttpContext available.
|
||||
// It's weird but is required for Deploy somehow.
|
||||
var parentScopeCallContext = scope?.ParentScope?.CallContext ?? false;
|
||||
if ((scope?.CallContext ?? false) && !parentScopeCallContext)
|
||||
{
|
||||
MoveCallContextScopeToHttpContext();
|
||||
MoveCallContextScopeContextToHttpContext();
|
||||
}
|
||||
else if ((!scope?.CallContext ?? false) && parentScopeCallContext)
|
||||
{
|
||||
MoveHttpContextScopeToCallContext();
|
||||
MoveHttpContextScopeContextToCallContext();
|
||||
}
|
||||
}
|
||||
public void PopAmbientScope() => _ambientScopeStack.Pop();
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -356,21 +130,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
throw new ArgumentNullException(nameof(scope));
|
||||
}
|
||||
|
||||
if (scope.CallContext || !SetHttpContextObject<IScope>(_scopeItemKey, scope, false))
|
||||
{
|
||||
// In this case, always ensure that the HttpContext items
|
||||
// is transfered to CallContext and then cleared since we
|
||||
// may be migrating context with the callContext = true flag.
|
||||
// This is a weird case when forcing callContext when HttpContext
|
||||
// is available. Required by Deploy.
|
||||
if (_requestCache.IsAvailable)
|
||||
{
|
||||
MoveHttpContextScopeToCallContext();
|
||||
MoveHttpContextScopeContextToCallContext();
|
||||
}
|
||||
|
||||
SetCallContextScope(scope);
|
||||
}
|
||||
_ambientScopeStack.Push(scope);
|
||||
}
|
||||
|
||||
public void PushAmbientScopeContext(IScopeContext? scopeContext)
|
||||
@@ -379,17 +139,10 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
throw new ArgumentNullException(nameof(scopeContext));
|
||||
}
|
||||
|
||||
SetHttpContextObject(_contextItemKey, scopeContext, false);
|
||||
SetCallContextScopeContext(scopeContext);
|
||||
_ambientContextStack.Push(scopeContext);
|
||||
}
|
||||
|
||||
public void PopAmbientScopeContext()
|
||||
{
|
||||
// pop stack from all contexts
|
||||
SetHttpContextObject<IScopeContext>(_contextItemKey, null, false);
|
||||
SetCallContextScopeContext(null);
|
||||
}
|
||||
public void PopAmbientScopeContext() => _ambientContextStack.Pop();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScope CreateDetachedScope(
|
||||
@@ -398,21 +151,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
IEventDispatcher? eventDispatcher = null,
|
||||
IScopedNotificationPublisher? scopedNotificationPublisher = null,
|
||||
bool? scopeFileSystems = null)
|
||||
=>
|
||||
new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
true,
|
||||
null,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
scopedNotificationPublisher,
|
||||
scopeFileSystems);
|
||||
=> new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopedNotificationPublisher, scopeFileSystems);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachScope(IScope other, bool callContext = false)
|
||||
@@ -457,20 +196,18 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
throw new InvalidOperationException("Ambient scope is not detachable.");
|
||||
}
|
||||
|
||||
PopAmbientScope(ambientScope);
|
||||
PopAmbientScope();
|
||||
PopAmbientScopeContext();
|
||||
|
||||
Scope? originalScope = AmbientScope;
|
||||
if (originalScope != ambientScope.OrigScope)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"The detatched scope ({ambientScope.GetDebugInfo()}) does not match the original ({originalScope?.GetDebugInfo()})");
|
||||
throw new InvalidOperationException($"The detatched scope ({ambientScope.GetDebugInfo()}) does not match the original ({originalScope?.GetDebugInfo()})");
|
||||
}
|
||||
|
||||
IScopeContext? originalScopeContext = AmbientContext;
|
||||
if (originalScopeContext != ambientScope.OrigContext)
|
||||
{
|
||||
throw new InvalidOperationException("The detatched scope context does not match the original");
|
||||
throw new InvalidOperationException($"The detatched scope context does not match the original");
|
||||
}
|
||||
|
||||
ambientScope.OrigScope = null;
|
||||
@@ -494,22 +231,7 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
IScopeContext? ambientContext = AmbientContext;
|
||||
ScopeContext? newContext = ambientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
false,
|
||||
newContext,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
notificationPublisher,
|
||||
scopeFileSystems,
|
||||
callContext,
|
||||
autoComplete);
|
||||
var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete);
|
||||
|
||||
// assign only if scope creation did not throw!
|
||||
PushAmbientScope(scope);
|
||||
@@ -517,34 +239,17 @@ namespace Umbraco.Cms.Infrastructure.Scoping
|
||||
{
|
||||
PushAmbientScopeContext(newContext);
|
||||
}
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
var nested = new Scope(
|
||||
this,
|
||||
_coreDebugSettings,
|
||||
_mediaFileManager,
|
||||
_eventAggregator,
|
||||
_loggerFactory.CreateLogger<Scope>(),
|
||||
_fileSystems,
|
||||
ambientScope,
|
||||
isolationLevel,
|
||||
repositoryCacheMode,
|
||||
eventDispatcher,
|
||||
notificationPublisher,
|
||||
scopeFileSystems,
|
||||
callContext,
|
||||
autoComplete);
|
||||
var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, notificationPublisher, scopeFileSystems, callContext, autoComplete);
|
||||
PushAmbientScope(nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IScopeContext? Context => AmbientContext;
|
||||
|
||||
// for testing
|
||||
internal ConcurrentStack<IScope>? GetCallContextScopeValue() => _scopeStack.Value;
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
// this code needs TLC
|
||||
//
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.42" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.43" />
|
||||
<PackageReference Include="MailKit" Version="3.2.0" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.407" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.422" />
|
||||
<PackageReference Include="Markdown" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
@@ -28,7 +28,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -37,10 +37,10 @@
|
||||
<PackageReference Include="ncrontab" Version="3.3.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="NPoco.SqlServer" Version="5.3.2" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="2.0.2" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="3.4.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="1.0.5" />
|
||||
@@ -48,15 +48,18 @@
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.2" />
|
||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.1" />
|
||||
<PackageReference Include="System.Text.Encodings.Web" Version="6.0.0" /> <!-- Explicit updated this nested dependency due to this https://github.com/dotnet/announcements/issues/178-->
|
||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="6.0.0" />
|
||||
<PackageReference Include="Examine.Core" Version="3.0.0-beta.9" />
|
||||
<PackageReference Include="Examine.Core" Version="3.0.0" />
|
||||
<PackageReference Include="Umbraco.Code" Version="2.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Update="Nerdbank.GitVersioning">
|
||||
<Version>3.5.107</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,110 +1,116 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="IUmbracoBuilder" /> for the Umbraco back office
|
||||
/// </summary>
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the supplementary localized texxt file sources from the various physical and virtual locations supported.
|
||||
/// Extension methods for <see cref="IUmbracoBuilder"/> for the Umbraco back office
|
||||
/// </summary>
|
||||
private static IUmbracoBuilder AddSupplemenataryLocalizedTextFileSources(this IUmbracoBuilder builder)
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
builder.Services.AddTransient(sp =>
|
||||
|
||||
/// <summary>
|
||||
/// Adds the supplementary localized texxt file sources from the various physical and virtual locations supported.
|
||||
/// </summary>
|
||||
private static IUmbracoBuilder AddSupplemenataryLocalizedTextFileSources(this IUmbracoBuilder builder)
|
||||
{
|
||||
return GetSupplementaryFileSources(
|
||||
sp.GetRequiredService<IWebHostEnvironment>());
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Loads the suplimentary localization files from plugins and user config
|
||||
/// </summary>
|
||||
private static IEnumerable<LocalizedTextServiceSupplementaryFileSource> GetSupplementaryFileSources(
|
||||
IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
IFileProvider webFileProvider = webHostEnvironment.WebRootFileProvider;
|
||||
IFileProvider contentFileProvider = webHostEnvironment.ContentRootFileProvider;
|
||||
|
||||
// gets all langs files in /app_plugins real or virtual locations
|
||||
IEnumerable<LocalizedTextServiceSupplementaryFileSource> pluginLangFileSources =
|
||||
GetPluginLanguageFileSources(webFileProvider, Constants.SystemDirectories.AppPlugins, false);
|
||||
|
||||
// user defined langs that overwrite the default, these should not be used by plugin creators
|
||||
var userConfigLangFolder = Constants.SystemDirectories.Config
|
||||
.TrimStart(Constants.CharArrays.Tilde);
|
||||
|
||||
IEnumerable<LocalizedTextServiceSupplementaryFileSource> userLangFileSources = contentFileProvider
|
||||
.GetDirectoryContents(userConfigLangFolder)
|
||||
.Where(x => x.IsDirectory && x.Name.InvariantEquals("lang"))
|
||||
.Select(x => new DirectoryInfo(x.PhysicalPath))
|
||||
.SelectMany(x => x.GetFiles("*.user.xml", SearchOption.TopDirectoryOnly))
|
||||
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true));
|
||||
|
||||
return pluginLangFileSources
|
||||
.Concat(userLangFileSources);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Loads the suplimentary localaization files via the file provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// locates all *.xml files in the lang folder of any sub folder of the one provided.
|
||||
/// e.g /app_plugins/plugin-name/lang/*.xml
|
||||
/// </remarks>
|
||||
private static IEnumerable<LocalizedTextServiceSupplementaryFileSource> GetPluginLanguageFileSources(
|
||||
IFileProvider fileProvider, string folder, bool overwriteCoreKeys)
|
||||
{
|
||||
// locate all the *.xml files inside Lang folders inside folders of the main folder
|
||||
// e.g. /app_plugins/plugin-name/lang/*.xml
|
||||
var fileSources = new List<LocalizedTextServiceSupplementaryFileSource>();
|
||||
|
||||
var pluginFolders = fileProvider.GetDirectoryContents(folder)
|
||||
.Where(x => x.IsDirectory).ToList();
|
||||
|
||||
foreach (IFileInfo pluginFolder in pluginFolders)
|
||||
{
|
||||
// get the full virtual path for the plugin folder
|
||||
var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name);
|
||||
|
||||
// get any lang folders in this plugin
|
||||
IEnumerable<IFileInfo> langFolders = fileProvider.GetDirectoryContents(pluginFolderPath)
|
||||
.Where(x => x.IsDirectory && x.Name.InvariantEquals("lang"));
|
||||
|
||||
// loop through the lang folder(s)
|
||||
// - there could be multiple on case sensitive file system
|
||||
foreach (IFileInfo langFolder in langFolders)
|
||||
builder.Services.AddTransient(sp =>
|
||||
{
|
||||
// get the full 'virtual' path of the lang folder
|
||||
var langFolderPath = WebPath.Combine(pluginFolderPath, langFolder.Name);
|
||||
return GetSupplementaryFileSources(
|
||||
sp.GetRequiredService<IWebHostEnvironment>());
|
||||
});
|
||||
|
||||
// request all the files out of the path, these will have physicalPath set.
|
||||
var files = fileProvider.GetDirectoryContents(langFolderPath)
|
||||
.Where(x => x.Name.InvariantEndsWith(".xml") && !string.IsNullOrEmpty(x.PhysicalPath))
|
||||
.Select(x => new FileInfo(x.PhysicalPath))
|
||||
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, overwriteCoreKeys))
|
||||
.ToList();
|
||||
return builder;
|
||||
}
|
||||
|
||||
// add any to our results
|
||||
if (files.Count > 0)
|
||||
|
||||
/// <summary>
|
||||
/// Loads the suplimentary localization files from plugins and user config
|
||||
/// </summary>
|
||||
private static IEnumerable<LocalizedTextServiceSupplementaryFileSource> GetSupplementaryFileSources(
|
||||
IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
IFileProvider webFileProvider = webHostEnvironment.WebRootFileProvider;
|
||||
IFileProvider contentFileProvider = webHostEnvironment.ContentRootFileProvider;
|
||||
|
||||
// gets all langs files in /app_plugins real or virtual locations
|
||||
IEnumerable<LocalizedTextServiceSupplementaryFileSource> pluginLangFileSources = GetPluginLanguageFileSources(webFileProvider, Cms.Core.Constants.SystemDirectories.AppPlugins, false);
|
||||
|
||||
// user defined langs that overwrite the default, these should not be used by plugin creators
|
||||
var userConfigLangFolder = Cms.Core.Constants.SystemDirectories.Config
|
||||
.TrimStart(Cms.Core.Constants.CharArrays.Tilde);
|
||||
|
||||
IEnumerable<LocalizedTextServiceSupplementaryFileSource> userLangFileSources = contentFileProvider.GetDirectoryContents(userConfigLangFolder)
|
||||
.Where(x => x.IsDirectory && x.Name.InvariantEquals("lang"))
|
||||
.Select(x => new DirectoryInfo(x.PhysicalPath))
|
||||
.SelectMany(x => x.GetFiles("*.user.xml", SearchOption.TopDirectoryOnly))
|
||||
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true));
|
||||
|
||||
return pluginLangFileSources
|
||||
.Concat(userLangFileSources);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Loads the suplimentary localaization files via the file provider.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// locates all *.xml files in the lang folder of any sub folder of the one provided.
|
||||
/// e.g /app_plugins/plugin-name/lang/*.xml
|
||||
/// </remarks>
|
||||
private static IEnumerable<LocalizedTextServiceSupplementaryFileSource> GetPluginLanguageFileSources(
|
||||
IFileProvider fileProvider, string folder, bool overwriteCoreKeys)
|
||||
{
|
||||
IEnumerable<IFileInfo> pluginFolders = fileProvider
|
||||
.GetDirectoryContents(folder)
|
||||
.Where(x => x.IsDirectory);
|
||||
|
||||
foreach (IFileInfo pluginFolder in pluginFolders)
|
||||
{
|
||||
// get the full virtual path for the plugin folder
|
||||
var pluginFolderPath = WebPath.Combine(folder, pluginFolder.Name);
|
||||
|
||||
// loop through the lang folder(s)
|
||||
// - there could be multiple on case sensitive file system
|
||||
foreach (var langFolder in GetLangFolderPaths(fileProvider, pluginFolderPath))
|
||||
{
|
||||
fileSources.AddRange(files);
|
||||
// request all the files out of the path, these will have physicalPath set.
|
||||
IEnumerable<FileInfo> localizationFiles = fileProvider
|
||||
.GetDirectoryContents(langFolder)
|
||||
.Where(x => !string.IsNullOrEmpty(x.PhysicalPath))
|
||||
.Where(x => x.Name.InvariantEndsWith(".xml"))
|
||||
.Select(x => new FileInfo(x.PhysicalPath));
|
||||
|
||||
foreach (FileInfo file in localizationFiles)
|
||||
{
|
||||
yield return new LocalizedTextServiceSupplementaryFileSource(file, overwriteCoreKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileSources;
|
||||
private static IEnumerable<string> GetLangFolderPaths(IFileProvider fileProvider, string path)
|
||||
{
|
||||
IEnumerable<IFileInfo> directories = fileProvider.GetDirectoryContents(path).Where(x => x.IsDirectory);
|
||||
|
||||
foreach (IFileInfo directory in directories)
|
||||
{
|
||||
var virtualPath = WebPath.Combine(path, directory.Name);
|
||||
|
||||
if (directory.Name.InvariantEquals("lang"))
|
||||
{
|
||||
yield return virtualPath;
|
||||
}
|
||||
|
||||
foreach (var nested in GetLangFolderPaths(fileProvider, virtualPath))
|
||||
{
|
||||
yield return nested;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
<PackageReference Include="Umbraco.Code" Version="2.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Update="Nerdbank.GitVersioning">
|
||||
<Version>3.5.107</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -11,5 +11,5 @@ public interface IVirtualPageController
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IPublishedContent" /> to use as the current page for the request
|
||||
/// </summary>
|
||||
IPublishedContent FindContent(ActionExecutingContext actionExecutingContext);
|
||||
IPublishedContent? FindContent(ActionExecutingContext actionExecutingContext);
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dazinator.Extensions.FileProviders" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="all" />
|
||||
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.2.22" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.0" />
|
||||
<PackageReference Include="Smidge.InMemory" Version="4.0.4" />
|
||||
<PackageReference Include="Smidge.Nuglify" Version="4.0.4" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Web" Version="2.0.1" />
|
||||
<PackageReference Include="Smidge.InMemory" Version="4.1.0" />
|
||||
<PackageReference Include="Smidge.Nuglify" Version="4.1.0" />
|
||||
<PackageReference Include="Umbraco.Code" Version="2.0.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ public abstract class UmbracoViewPage<TModel> : RazorPage<TModel>
|
||||
{
|
||||
// filter / add preview banner
|
||||
// ASP.NET default value is text/html
|
||||
if (Context.Response.ContentType.InvariantContains("text/html"))
|
||||
if (Context.Response?.ContentType?.InvariantContains("text/html") ?? false)
|
||||
{
|
||||
if (((UmbracoContext?.IsDebug ?? false) || (UmbracoContext?.InPreviewMode ?? false))
|
||||
&& tagHelperOutput.TagName != null
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Umbraco.Cms.Web.UI
|
||||
{
|
||||
public class Program
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Web.UI
|
||||
{
|
||||
public class Startup
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.5" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
<PackageReference Include="Umbraco.Code" Version="2.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Update="Nerdbank.GitVersioning">
|
||||
<Version>3.5.107</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Enable multi-level merging with src -->
|
||||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../src/'))" />
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<PackageType>Template</PackageType>
|
||||
@@ -14,7 +14,7 @@
|
||||
<NoDefaultExcludes>true</NoDefaultExcludes>
|
||||
<IncludeSymbols>false</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="icon.png" Visible="false" />
|
||||
<Content Include="UmbracoPackage\**" Exclude="bin;obj" />
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">Umbraco.Cms.Web.UI</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">Umbraco.Cms.Web.UI</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Umbraco.Cms" Version="UMBRACO_VERSION_FROM_TEMPLATE" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Umbraco.Cms" Version="UMBRACO_VERSION_FROM_TEMPLATE" />
|
||||
</ItemGroup>
|
||||
<!-- Force Windows to use ICU. Otherwise Windows 10 2019H1+ will do it, but older Windows 10 and most, if not all, Windows Server editions will run NLS -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
|
||||
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" Condition="$(RuntimeIdentifier.StartsWith('linux')) or $(RuntimeIdentifier.StartsWith('win')) or ('$(RuntimeIdentifier)' == '' and !$([MSBuild]::IsOSPlatform('osx')))" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Force Windows to use ICU. Otherwise Windows 10 2019H1+ will do it, but older Windows 10 and most, if not all, Windows Server editions will run NLS -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
|
||||
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" Condition="$(RuntimeIdentifier.StartsWith('linux')) or $(RuntimeIdentifier.StartsWith('win')) or ('$(RuntimeIdentifier)' == '' and !$([MSBuild]::IsOSPlatform('osx')))" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\build\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
|
||||
<ItemGroup Condition="'$(PackageProjectName)' != ''">
|
||||
<ProjectReference Include="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\build\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.targets" Condition="'$(PackageProjectName)' != ''" />
|
||||
<ItemGroup Condition="'$(PackageProjectName)' != ''">
|
||||
<ProjectReference Include="..\PACKAGE_PROJECT_NAME_FROM_TEMPLATE\PACKAGE_PROJECT_NAME_FROM_TEMPLATE.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<CopyRazorGenerateFilesToPublishDirectory>true</CopyRazorGenerateFilesToPublishDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CopyRazorGenerateFilesToPublishDirectory>true</CopyRazorGenerateFilesToPublishDirectory>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Keep this as false if ModelsBuilder mode is InMemoryAuto -->
|
||||
<PropertyGroup>
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Keep this as false if ModelsBuilder mode is InMemoryAuto -->
|
||||
<PropertyGroup>
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<Version>6.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Moq">
|
||||
<Version>4.17.2</Version>
|
||||
<Version>4.18.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
|
||||
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.2.22" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="Moq" Version="4.18.1" />
|
||||
<PackageReference Include="AutoFixture.NUnit3" Version="4.17.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -226,56 +226,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedMigrateScope()
|
||||
{
|
||||
// Get the request cache mock and re-configure it to be available and used
|
||||
var requestCacheDictionary = new Dictionary<string, object>();
|
||||
IRequestCache requestCache = AppCaches.RequestCache;
|
||||
var requestCacheMock = Mock.Get(requestCache);
|
||||
requestCacheMock
|
||||
.Setup(x => x.IsAvailable)
|
||||
.Returns(true);
|
||||
requestCacheMock
|
||||
.Setup(x => x.Set(It.IsAny<string>(), It.IsAny<object>()))
|
||||
.Returns((string key, object val) =>
|
||||
{
|
||||
requestCacheDictionary.Add(key, val);
|
||||
return true;
|
||||
});
|
||||
requestCacheMock
|
||||
.Setup(x => x.Get(It.IsAny<string>()))
|
||||
.Returns((string key) => requestCacheDictionary.TryGetValue(key, out var val) ? val : null);
|
||||
|
||||
ScopeProvider scopeProvider = ScopeProvider;
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
|
||||
using (IScope scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.IsInstanceOf<Scope>(scope);
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||
|
||||
using (IScope nested = scopeProvider.CreateScope(callContext: true))
|
||||
{
|
||||
Assert.IsInstanceOf<Scope>(nested);
|
||||
Assert.IsNotNull(scopeProvider.AmbientScope);
|
||||
Assert.AreSame(nested, scopeProvider.AmbientScope);
|
||||
Assert.AreSame(scope, ((Scope)nested).ParentScope);
|
||||
|
||||
// it's moved over to call context
|
||||
ConcurrentStack<IScope> callContextScope = scopeProvider.GetCallContextScopeValue();
|
||||
|
||||
Assert.IsNotNull(callContextScope);
|
||||
Assert.AreEqual(2, callContextScope.Count);
|
||||
}
|
||||
|
||||
// it's naturally back in http context
|
||||
}
|
||||
|
||||
Assert.IsNull(scopeProvider.AmbientScope);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedCreateScopeContext()
|
||||
{
|
||||
|
||||
@@ -134,11 +134,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
ConcurrentStack<IScope>
|
||||
currentStack = ((ScopeProvider)ScopeProvider).GetCallContextScopeValue();
|
||||
log.LogInformation("[{ThreadId}] Current Stack? {CurrentStack}",
|
||||
Thread.CurrentThread.ManagedThreadId, currentStack?.Count);
|
||||
|
||||
// NOTE: This is NULL because we have supressed the execution context flow.
|
||||
// If we don't do that we will get various exceptions because we're trying to run concurrent threads
|
||||
// against an ambient context which cannot be done due to the rules of scope creation and completion.
|
||||
@@ -234,11 +229,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
ConcurrentStack<IScope>
|
||||
currentStack = ((ScopeProvider)ScopeProvider).GetCallContextScopeValue();
|
||||
log.LogInformation("[{ThreadId}] Current Stack? {CurrentStack}",
|
||||
Thread.CurrentThread.ManagedThreadId, currentStack?.Count);
|
||||
|
||||
// NOTE: This is NULL because we have supressed the execution context flow.
|
||||
// If we don't do that we will get various exceptions because we're trying to run concurrent threads
|
||||
// against an ambient context which cannot be done due to the rules of scope creation and completion.
|
||||
|
||||
@@ -85,13 +85,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Examine.Lucene" Version="3.0.0-beta.9" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.4" />
|
||||
<PackageReference Include="Examine.Lucene" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.5" />
|
||||
<PackageReference Include="Bogus" Version="34.0.2" />
|
||||
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="Moq" Version="4.18.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components
|
||||
Mock.Of<IServiceProvider>(),
|
||||
Options.Create(new ContentSettings()));
|
||||
IEventAggregator eventAggregator = Mock.Of<IEventAggregator>();
|
||||
var scopeProvider = new ScopeProvider(Mock.Of<IDistributedLockingMechanismFactory>(),f , fs, new TestOptionsMonitor<CoreDebugSettings>(coreDebug), mediaFileManager, loggerFactory, NoAppCache.Instance, eventAggregator);
|
||||
var scopeProvider = new ScopeProvider(new AmbientScopeStack(), new AmbientScopeContextStack(), Mock.Of<IDistributedLockingMechanismFactory>(),f , fs, new TestOptionsMonitor<CoreDebugSettings>(coreDebug), mediaFileManager, loggerFactory, eventAggregator);
|
||||
|
||||
mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger);
|
||||
mock.Setup(x => x.GetService(typeof(ILogger<ComponentCollection>))).Returns(loggerFactory.CreateLogger<ComponentCollection>);
|
||||
|
||||
@@ -93,13 +93,14 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping
|
||||
eventAggregatorMock = new Mock<IEventAggregator>();
|
||||
|
||||
return new ScopeProvider(
|
||||
new AmbientScopeStack(),
|
||||
new AmbientScopeContextStack(),
|
||||
Mock.Of<IDistributedLockingMechanismFactory>(),
|
||||
Mock.Of<IUmbracoDatabaseFactory>(),
|
||||
fileSystems,
|
||||
new TestOptionsMonitor<CoreDebugSettings>(new CoreDebugSettings()),
|
||||
mediaFileManager,
|
||||
loggerFactory,
|
||||
Mock.Of<IRequestCache>(),
|
||||
eventAggregatorMock.Object
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,13 +71,14 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping
|
||||
sqlContext.Setup(x => x.SqlSyntax).Returns(syntaxProviderMock.Object);
|
||||
|
||||
return new ScopeProvider(
|
||||
new AmbientScopeStack(),
|
||||
new AmbientScopeContextStack(),
|
||||
lockingMechanismFactory.Object,
|
||||
databaseFactory.Object,
|
||||
fileSystems,
|
||||
new TestOptionsMonitor<CoreDebugSettings>(new CoreDebugSettings()),
|
||||
mediaFileManager,
|
||||
loggerFactory,
|
||||
Mock.Of<IRequestCache>(),
|
||||
Mock.Of<IEventAggregator>());
|
||||
}
|
||||
|
||||
@@ -579,5 +580,34 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping
|
||||
Assert.IsNull(realScope.GetReadLocks());
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Depth_WhenRootScope_ReturnsZero()
|
||||
{
|
||||
var scopeProvider = GetScopeProvider(out var syntaxProviderMock);
|
||||
|
||||
using (var scope = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.AreEqual(0,scope.Depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void Depth_WhenChildScope_ReturnsDepth()
|
||||
{
|
||||
var scopeProvider = GetScopeProvider(out var syntaxProviderMock);
|
||||
|
||||
using (scopeProvider.CreateScope())
|
||||
{
|
||||
using (scopeProvider.CreateScope())
|
||||
{
|
||||
using (var c2 = scopeProvider.CreateScope())
|
||||
{
|
||||
Assert.AreEqual(2, c2.Depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||
<PackageReference Include="AngleSharp" Version="0.17.1" />
|
||||
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
|
||||
Reference in New Issue
Block a user