Hotfix: Move allow edit invariant from non default setting to content settings (#12960)

* Use ContentSettings instead of SecuritySettings for AllowEditInvariantFromNonDefault

* Make it backwards compatible

(cherry picked from commit 3846c75cc6)
This commit is contained in:
Mole
2022-09-07 14:38:54 +02:00
committed by Sebastiaan Janssen
parent aa7a7c4691
commit 48c981d31f
8 changed files with 105 additions and 53 deletions

View File

@@ -157,6 +157,7 @@ public class ContentSettings
internal const bool StaticHideBackOfficeLogo = false;
internal const bool StaticDisableDeleteWhenReferenced = false;
internal const bool StaticDisableUnpublishWhenReferenced = false;
internal const bool StaticAllowEditInvariantFromNonDefault = false;
/// <summary>
/// Gets or sets a value for the content notification settings.
@@ -242,4 +243,10 @@ public class ContentSettings
/// Get or sets the model representing the global content version cleanup policy
/// </summary>
public ContentVersionCleanupPolicySettings ContentVersionCleanupPolicy { get; set; } = new();
/// <summary>
/// Gets or sets a value indicating whether to allow editing invariant properties from a non-default language variation.
/// </summary>
[DefaultValue(StaticAllowEditInvariantFromNonDefault)]
public bool AllowEditInvariantFromNonDefault { get; set; } = StaticAllowEditInvariantFromNonDefault;
}

View File

@@ -86,9 +86,10 @@ public class SecuritySettings
[DefaultValue(StaticUserBypassTwoFactorForExternalLogins)]
public bool UserBypassTwoFactorForExternalLogins { get; set; } = StaticUserBypassTwoFactorForExternalLogins;
/// <summary>
/// Gets or sets a value indicating whether to allow editing invariant properties from a non-default language variation.
/// </summary>
[DefaultValue(StaticAllowEditInvariantFromNonDefault)]
public bool AllowEditInvariantFromNonDefault { get; set; } = StaticAllowEditInvariantFromNonDefault;
/// <summary>
/// Gets or sets a value indicating whether to allow editing invariant properties from a non-default language variation.
/// </summary>
[Obsolete("Use ContentSettings.AllowEditFromInvariant instead")]
[DefaultValue(StaticAllowEditInvariantFromNonDefault)]
public bool AllowEditInvariantFromNonDefault { get; set; } = StaticAllowEditInvariantFromNonDefault;
}

View File

@@ -1,4 +1,5 @@
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
@@ -104,6 +105,20 @@ public static partial class UmbracoBuilderExtensions
builder.Services.Configure<RequestHandlerSettings>(options => options.MergeReplacements(builder.Config));
// TODO: Remove this in V12
// This is to make the move of the AllowEditInvariantFromNonDefault setting from SecuritySettings to ContentSettings backwards compatible
// If there is a value in security settings, but no value in content setting we'll use that value, otherwise content settings always wins.
builder.Services.Configure<ContentSettings>(settings =>
{
var securitySettingsValue = builder.Config.GetSection($"{Constants.Configuration.ConfigSecurity}").GetValue<bool?>(nameof(SecuritySettings.AllowEditInvariantFromNonDefault));
var contentSettingsValue = builder.Config.GetSection($"{Constants.Configuration.ConfigContent}").GetValue<bool?>(nameof(ContentSettings.AllowEditInvariantFromNonDefault));
if (securitySettingsValue is not null && contentSettingsValue is null)
{
settings.AllowEditInvariantFromNonDefault = securitySettingsValue.Value;
}
});
return builder;
}
}

View File

@@ -324,7 +324,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddUnique<IPropertyTypeUsageService, PropertyTypeUsageService>();
Services.AddUnique<IDataTypeUsageService, DataTypeUsageService>();
Services.AddUnique<ICultureImpactFactory, CultureImpactFactory>();
Services.AddUnique<ICultureImpactFactory>(provider => new CultureImpactFactory(provider.GetRequiredService<IOptionsMonitor<ContentSettings>>()));
}
}
}

View File

@@ -20,7 +20,7 @@ public class ContentVariantMapper
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private readonly IContentService _contentService;
private readonly IUserService _userService;
private SecuritySettings _securitySettings;
private ContentSettings _contentSettings;
public ContentVariantMapper(
ILocalizationService localizationService,
@@ -28,17 +28,36 @@ public class ContentVariantMapper
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IContentService contentService,
IUserService userService,
IOptionsMonitor<SecuritySettings> securitySettings)
IOptionsMonitor<ContentSettings> contentSettings)
{
_localizationService = localizationService ?? throw new ArgumentNullException(nameof(localizationService));
_localizedTextService = localizedTextService ?? throw new ArgumentNullException(nameof(localizedTextService));
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
_contentService = contentService;
_userService = userService;
_securitySettings = securitySettings.CurrentValue;
securitySettings.OnChange(settings => _securitySettings = settings);
_contentSettings = contentSettings.CurrentValue;
contentSettings.OnChange(settings => _contentSettings = settings);
}
[Obsolete("Use constructor that takes all parameters instead")]
public ContentVariantMapper(
ILocalizationService localizationService,
ILocalizedTextService localizedTextService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IContentService contentService,
IUserService userService,
IOptionsMonitor<SecuritySettings> securitySettings)
: this(
localizationService,
localizedTextService,
backOfficeSecurityAccessor,
contentService,
userService,
StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<ContentSettings>>())
{
}
[Obsolete("Use constructor that takes all parameters instead")]
public ContentVariantMapper(ILocalizationService localizationService, ILocalizedTextService localizedTextService)
: this(
localizationService,
@@ -244,7 +263,7 @@ public class ContentVariantMapper
if (variantDisplay.Language is null)
{
var defaultLanguageId = _localizationService.GetDefaultLanguageId();
if (_securitySettings.AllowEditInvariantFromNonDefault || (defaultLanguageId.HasValue && group.HasAccessToLanguage(defaultLanguageId.Value)))
if (_contentSettings.AllowEditInvariantFromNonDefault || (defaultLanguageId.HasValue && group.HasAccessToLanguage(defaultLanguageId.Value)))
{
hasAccess = true;
}

View File

@@ -1,25 +1,33 @@
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Services;
public class CultureImpactFactory : ICultureImpactFactory
{
private SecuritySettings _securitySettings;
private ContentSettings _contentSettings;
public CultureImpactFactory(IOptionsMonitor<SecuritySettings> securitySettings)
public CultureImpactFactory(IOptionsMonitor<ContentSettings> contentSettings)
{
_securitySettings = securitySettings.CurrentValue;
_contentSettings = contentSettings.CurrentValue;
securitySettings.OnChange(x => _securitySettings = x);
contentSettings.OnChange(x => _contentSettings = x);
}
[Obsolete("Use constructor that takes IOptionsMonitor<SecuritySettings> instead. Scheduled for removal in V12")]
public CultureImpactFactory(IOptionsMonitor<SecuritySettings> securitySettings)
: this(StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<ContentSettings>>())
{
}
/// <inheritdoc/>
public CultureImpact? Create(string? culture, bool isDefault, IContent content)
{
TryCreate(culture, isDefault, content.ContentType.Variations, true, _securitySettings.AllowEditInvariantFromNonDefault, out CultureImpact? impact);
TryCreate(culture, isDefault, content.ContentType.Variations, true, _contentSettings.AllowEditInvariantFromNonDefault, out CultureImpact? impact);
return impact;
}
@@ -48,7 +56,7 @@ public class CultureImpactFactory : ICultureImpactFactory
throw new ArgumentException("Culture \"*\" is not explicit.");
}
return new CultureImpact(culture, isDefault, _securitySettings.AllowEditInvariantFromNonDefault);
return new CultureImpact(culture, isDefault, _contentSettings.AllowEditInvariantFromNonDefault);
}
/// <inheritdoc/>

View File

@@ -569,7 +569,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
{"minimumPasswordNonAlphaNum", _memberPasswordConfigurationSettings.GetMinNonAlphaNumericChars()},
{"sanitizeTinyMce", _globalSettings.SanitizeTinyMce},
{"dataTypesCanBeChanged", _dataTypesSettings.CanBeChanged.ToString()},
{"allowEditInvariantFromNonDefault", _securitySettings.AllowEditInvariantFromNonDefault},
{"allowEditInvariantFromNonDefault", _contentSettings.AllowEditInvariantFromNonDefault},
}
},
{

View File

@@ -13,24 +13,25 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Models;
[TestFixture]
public class CultureImpactTests
{
private CultureImpactFactory BasicImpactFactory => createCultureImpactService();
private CultureImpactFactory BasicImpactFactory => createCultureImpactService();
[Test]
public void Get_Culture_For_Invariant_Errors()
{
var result = BasicImpactFactory.GetCultureForInvariantErrors(
var result = BasicImpactFactory.GetCultureForInvariantErrors(
Mock.Of<IContent>(x => x.Published == true),
new[] { "en-US", "fr-FR" },
"en-US");
Assert.AreEqual("en-US", result); // default culture is being saved so use it
result = BasicImpactFactory.GetCultureForInvariantErrors(
result = BasicImpactFactory.GetCultureForInvariantErrors(
Mock.Of<IContent>(x => x.Published == false),
new[] { "fr-FR" },
"en-US");
Assert.AreEqual("fr-FR", result); // default culture not being saved with not published version, use the first culture being saved
Assert.AreEqual("fr-FR",
result); // default culture not being saved with not published version, use the first culture being saved
result = BasicImpactFactory.GetCultureForInvariantErrors(
result = BasicImpactFactory.GetCultureForInvariantErrors(
Mock.Of<IContent>(x => x.Published == true),
new[] { "fr-FR" },
"en-US");
@@ -70,7 +71,7 @@ public class CultureImpactTests
[Test]
public void Explicit_Default_Culture()
{
var impact = BasicImpactFactory.ImpactExplicit("en-US", true);
var impact = BasicImpactFactory.ImpactExplicit("en-US", true);
Assert.AreEqual(impact.Culture, "en-US");
@@ -85,7 +86,7 @@ public class CultureImpactTests
[Test]
public void Explicit_NonDefault_Culture()
{
var impact = BasicImpactFactory.ImpactExplicit("en-US", false);
var impact = BasicImpactFactory.ImpactExplicit("en-US", false);
Assert.AreEqual(impact.Culture, "en-US");
@@ -100,10 +101,11 @@ public class CultureImpactTests
[Test]
public void TryCreate_Explicit_Default_Culture()
{
var success = BasicImpactFactory.TryCreate("en-US", true, ContentVariation.Culture, false, false, out var impact);
var success =
BasicImpactFactory.TryCreate("en-US", true, ContentVariation.Culture, false, false, out var impact);
Assert.IsTrue(success);
Assert.IsNotNull(impact);
Assert.IsNotNull(impact);
Assert.AreEqual(impact.Culture, "en-US");
Assert.IsTrue(impact.ImpactsInvariantProperties);
@@ -117,10 +119,11 @@ public class CultureImpactTests
[Test]
public void TryCreate_Explicit_NonDefault_Culture()
{
var success = BasicImpactFactory.TryCreate("en-US", false, ContentVariation.Culture, false, false, out var impact);
var success =
BasicImpactFactory.TryCreate("en-US", false, ContentVariation.Culture, false, false, out var impact);
Assert.IsTrue(success);
Assert.IsNotNull(impact);
Assert.IsNotNull(impact);
Assert.AreEqual(impact.Culture, "en-US");
Assert.IsFalse(impact.ImpactsInvariantProperties);
@@ -137,10 +140,10 @@ public class CultureImpactTests
var success = BasicImpactFactory.TryCreate("*", false, ContentVariation.Nothing, false, false, out var impact);
Assert.IsTrue(success);
Assert.IsNotNull(impact);
Assert.IsNotNull(impact);
Assert.AreEqual(impact.Culture, null);
Assert.AreSame(BasicImpactFactory.ImpactInvariant(), impact);
Assert.AreSame(BasicImpactFactory.ImpactInvariant(), impact);
}
[Test]
@@ -149,10 +152,10 @@ public class CultureImpactTests
var success = BasicImpactFactory.TryCreate("*", false, ContentVariation.Culture, false, false, out var impact);
Assert.IsTrue(success);
Assert.IsNotNull(impact);
Assert.IsNotNull(impact);
Assert.AreEqual(impact.Culture, "*");
Assert.AreSame(BasicImpactFactory.ImpactAll(), impact);
Assert.AreSame(BasicImpactFactory.ImpactAll(), impact);
}
[Test]
@@ -168,28 +171,27 @@ public class CultureImpactTests
var success = BasicImpactFactory.TryCreate(null, false, ContentVariation.Nothing, false, false, out var impact);
Assert.IsTrue(success);
Assert.AreSame(BasicImpactFactory.ImpactInvariant(), impact);
}
[Test]
[TestCase(true)]
[TestCase(false)]
public void Edit_Invariant_From_Non_Default_Impacts_Invariant_Properties(bool allowEditInvariantFromNonDefault)
{
var sut = createCultureImpactService(new SecuritySettings { AllowEditInvariantFromNonDefault = allowEditInvariantFromNonDefault });
var impact = sut.ImpactExplicit("da", false);
Assert.AreEqual(allowEditInvariantFromNonDefault, impact.ImpactsAlsoInvariantProperties);
Assert.AreSame(BasicImpactFactory.ImpactInvariant(), impact);
}
private CultureImpactFactory createCultureImpactService(SecuritySettings securitySettings = null)
[Test]
[TestCase(true)]
[TestCase(false)]
public void Edit_Invariant_From_Non_Default_Impacts_Invariant_Properties(bool allowEditInvariantFromNonDefault)
{
var sut = createCultureImpactService(new ContentSettings
{
securitySettings ??= new SecuritySettings
{
AllowEditInvariantFromNonDefault = false,
};
AllowEditInvariantFromNonDefault = allowEditInvariantFromNonDefault
});
var impact = sut.ImpactExplicit("da", false);
return new CultureImpactFactory(new TestOptionsMonitor<SecuritySettings>(securitySettings));
}
Assert.AreEqual(allowEditInvariantFromNonDefault, impact.ImpactsAlsoInvariantProperties);
}
private CultureImpactFactory createCultureImpactService(ContentSettings contentSettings = null)
{
contentSettings ??= new ContentSettings { AllowEditInvariantFromNonDefault = false, };
return new CultureImpactFactory(new TestOptionsMonitor<ContentSettings>(contentSettings));
}
}