Publish invariant properties (#12386)

* Publish invariants from non default

* Add culture impact service

* Use the new culture impact service instead of newing up culture impacts

* Only publish invariant properties on non-defaults with invariant culture

Essentially we want to be able to fall back to the default culture for the variant properties if a document type is made invariant, not whatever culture was published last.

* Move creation logic into the service

* Make creation method names consistent

* Fix tests

We compare the cultures directly, so they have to be the same object instance unfortunately

* Add test for the new setting

* Add new config to server variables

* Fix test setup

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Mole
2022-05-12 12:41:51 +02:00
committed by GitHub
parent 6ddce4b151
commit 3f0ff6a752
11 changed files with 462 additions and 73 deletions

View File

@@ -235,51 +235,67 @@ namespace Umbraco.Extensions
/// culture(s). The method may fail if required names are not set, but it does NOT validate property data</returns>
public static bool PublishCulture(this IContent content, CultureImpact? impact)
{
if (impact == null) throw new ArgumentNullException(nameof(impact));
if (impact == null)
{
throw new ArgumentNullException(nameof(impact));
}
// the variation should be supported by the content type properties
// if the content type is invariant, only '*' and 'null' is ok
// if the content type varies, everything is ok because some properties may be invariant
if (!content.ContentType.SupportsPropertyVariation(impact.Culture, "*", true))
{
throw new NotSupportedException($"Culture \"{impact.Culture}\" is not supported by content type \"{content.ContentType.Alias}\" with variation \"{content.ContentType.Variations}\".");
}
// set names
if (impact.ImpactsAllCultures)
{
foreach (var c in content.AvailableCultures) // does NOT contain the invariant culture
foreach (var culture in content.AvailableCultures) // does NOT contain the invariant culture
{
var name = content.GetCultureName(c);
var name = content.GetCultureName(culture);
if (string.IsNullOrWhiteSpace(name))
{
return false;
content.SetPublishInfo(c, name, DateTime.Now);
}
content.SetPublishInfo(culture, name, DateTime.Now);
}
}
else if (impact.ImpactsOnlyInvariantCulture)
{
if (string.IsNullOrWhiteSpace(content.Name))
{
return false;
}
// PublishName set by repository - nothing to do here
}
else if (impact.ImpactsExplicitCulture)
{
var name = content.GetCultureName(impact.Culture);
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
content.SetPublishInfo(impact.Culture, name, DateTime.Now);
}
// set values
// property.PublishValues only publishes what is valid, variation-wise,
// but accepts any culture arg: null, all, specific
foreach (var property in content.Properties)
foreach (IProperty property in content.Properties)
{
// for the specified culture (null or all or specific)
property.PublishValues(impact.Culture);
// maybe the specified culture did not impact the invariant culture, so PublishValues
// above would skip it, yet it *also* impacts invariant properties
if (impact.ImpactsAlsoInvariantProperties)
if (impact.ImpactsAlsoInvariantProperties &&
(property.PropertyType.VariesByCulture() is false || impact.ImpactsOnlyDefaultCulture))
{
property.PublishValues(null);
}
}
content.PublishedState = PublishedState.Publishing;

View File

@@ -1,6 +1,4 @@
using System;
using System.Linq;
using Umbraco.Extensions;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Models
{
@@ -21,6 +19,7 @@ namespace Umbraco.Cms.Core.Models
/// <param name="savingCultures"></param>
/// <param name="defaultCulture"></param>
/// <returns></returns>
[Obsolete("Use CultureImpactService instead, scheduled for removal in v12")]
public static string? GetCultureForInvariantErrors(IContent? content, string?[] savingCultures, string? defaultCulture)
{
if (content == null) throw new ArgumentNullException(nameof(content));
@@ -42,17 +41,24 @@ namespace Umbraco.Cms.Core.Models
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
private CultureImpact(string? culture, bool isDefault = false)
/// <param name="allowEditInvariantFromNonDefault">A value indicating if publishing invariant properties from non-default language.</param>
internal CultureImpact(string? culture, bool isDefault = false, bool allowEditInvariantFromNonDefault = false)
{
if (culture != null && culture.IsNullOrWhiteSpace())
{
throw new ArgumentException("Culture \"\" is not valid here.");
}
Culture = culture;
if ((culture == null || culture == "*") && isDefault)
{
throw new ArgumentException("The invariant or 'all' culture can not be the default culture.");
}
ImpactsOnlyDefaultCulture = isDefault;
AllowEditInvariantFromNonDefault = allowEditInvariantFromNonDefault;
}
/// <summary>
@@ -70,14 +76,24 @@ namespace Umbraco.Cms.Core.Models
/// </summary>
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
/// <param name="allowEditInvariantFromNonDefault">A value indicating if publishing invariant properties from non-default language.</param>
[Obsolete("Use ICultureImpactService instead.")]
public static CultureImpact Explicit(string? culture, bool isDefault)
{
if (culture == null)
{
throw new ArgumentException("Culture <null> is not explicit.");
}
if (culture.IsNullOrWhiteSpace())
{
throw new ArgumentException("Culture \"\" is not explicit.");
}
if (culture == "*")
{
throw new ArgumentException("Culture \"*\" is not explicit.");
}
return new CultureImpact(culture, isDefault);
}
@@ -89,13 +105,15 @@ namespace Umbraco.Cms.Core.Models
/// <param name="culture">The culture code.</param>
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
/// <param name="content">The content item.</param>
/// <param name="allowEditInvariantFromNonDefault">A value indicating if publishing invariant properties from non-default language.</param>
/// <remarks>
/// <para>Validates that the culture is compatible with the variation.</para>
/// </remarks>
[Obsolete("Use ICultureImpactService instead, scheduled for removal in V12")]
public static CultureImpact? Create(string culture, bool isDefault, IContent content)
{
// throws if not successful
TryCreate(culture, isDefault, content.ContentType.Variations, true, out var impact);
TryCreate(culture, isDefault, content.ContentType.Variations, true, false, out CultureImpact? impact);
return impact;
}
@@ -107,12 +125,14 @@ namespace Umbraco.Cms.Core.Models
/// <param name="isDefault">A value indicating whether the culture is the default culture.</param>
/// <param name="variation">A content variation.</param>
/// <param name="throwOnFail">A value indicating whether to throw if the impact cannot be created.</param>
/// <param name="editInvariantFromNonDefault">A value indicating if publishing invariant properties from non-default language.</param>
/// <param name="impact">The impact if it could be created, otherwise null.</param>
/// <returns>A value indicating whether the impact could be created.</returns>
/// <remarks>
/// <para>Validates that the culture is compatible with the variation.</para>
/// </remarks>
internal static bool TryCreate(string culture, bool isDefault, ContentVariation variation, bool throwOnFail, out CultureImpact? impact)
// TODO: Remove this once Create() can be removed (V12), this already lives in CultureImpactService
internal static bool TryCreate(string culture, bool isDefault, ContentVariation variation, bool throwOnFail, bool editInvariantFromNonDefault, out CultureImpact? impact)
{
impact = null;
@@ -172,7 +192,7 @@ namespace Umbraco.Cms.Core.Models
}
// return specific impact
impact = new CultureImpact(culture, isDefault);
impact = new CultureImpact(culture, isDefault, editInvariantFromNonDefault);
return true;
}
@@ -184,6 +204,8 @@ namespace Umbraco.Cms.Core.Models
/// </remarks>
public string? Culture { get; }
public bool AllowEditInvariantFromNonDefault { get; }
/// <summary>
/// Gets a value indicating whether this impact impacts all cultures, including,
/// indirectly, the invariant culture.
@@ -222,7 +244,7 @@ namespace Umbraco.Cms.Core.Models
/// </summary>
public bool ImpactsAlsoInvariantProperties => !ImpactsOnlyInvariantCulture &&
!ImpactsAllCultures &&
ImpactsOnlyDefaultCulture;
(ImpactsOnlyDefaultCulture || AllowEditInvariantFromNonDefault);
public Behavior CultureBehavior
{