Fixes mandatory validation issue when creating a new document and a mandatory language has validation problems
This commit is contained in:
@@ -12,6 +12,31 @@ namespace Umbraco.Tests.Web
|
||||
public class ModelStateExtensionsTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Get_Cultures_With_Errors()
|
||||
{
|
||||
var ms = new ModelStateDictionary();
|
||||
var localizationService = new Mock<ILocalizationService>();
|
||||
localizationService.Setup(x => x.GetDefaultLanguageIsoCode()).Returns("en-US");
|
||||
|
||||
ms.AddPropertyError(new ValidationResult("no header image"), "headerImage", null); //invariant property
|
||||
ms.AddPropertyError(new ValidationResult("title missing"), "title", "en-US"); //variant property
|
||||
|
||||
var result = ms.GetCulturesWithErrors(localizationService.Object);
|
||||
|
||||
//even though there are 2 errors, they are both for en-US since that is the default language and one of the errors is for an invariant property
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual("en-US", result[0]);
|
||||
|
||||
ms = new ModelStateDictionary();
|
||||
ms.AddCultureValidationError("en-US", "generic culture error");
|
||||
|
||||
result = ms.GetCulturesWithErrors(localizationService.Object);
|
||||
|
||||
Assert.AreEqual(1, result.Count);
|
||||
Assert.AreEqual("en-US", result[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Cultures_With_Property_Errors()
|
||||
{
|
||||
|
||||
@@ -1242,6 +1242,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
%0% can not be published, because a parent page is not published.
|
||||
]]></key>
|
||||
<key alias="contentPublishedFailedByMissingName"><![CDATA[%0% can not be published, because its missing a name.]]></key>
|
||||
<key alias="contentPublishedFailedReqCultureValidationError">Validation failed for required language '%0%'. The language was saved but not published.</key>
|
||||
<key alias="inProgress">Publishing in progress - please wait...</key>
|
||||
<key alias="inProgressCounter">%0% out of %1% pages have been published...</key>
|
||||
<key alias="nodePublish">%0% has been published</key>
|
||||
@@ -1409,7 +1410,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="deleteUserSuccess">User %0% was deleted</key>
|
||||
<key alias="resendInviteHeader">Invite user</key>
|
||||
<key alias="resendInviteSuccess">Invitation has been re-sent to %0%</key>
|
||||
<key alias="contentReqCulturePublishError">Cannot publish the document since the required '%0%' is not published</key>
|
||||
<key alias="contentReqCulturePublishError">Cannot publish the document since the required '%0%' is not published</key>
|
||||
<key alias="contentCultureValidationError">Validation failed for language '%0%'</key>
|
||||
<key alias="documentTypeExportedSuccess">Document type was exported to file</key>
|
||||
<key alias="documentTypeExportedError">An error occurred while exporting the document type</key>
|
||||
|
||||
@@ -689,7 +689,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
if (variantCount > 1)
|
||||
{
|
||||
var cultureErrors = ModelState.GetCulturesWithPropertyErrors(Services.LocalizationService);
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService);
|
||||
foreach (var c in contentItem.Variants.Where(x => x.Save && !cultureErrors.Contains(x.Culture)).Select(x => x.Culture).ToArray())
|
||||
{
|
||||
AddSuccessNotification(notifications, c,
|
||||
@@ -882,7 +882,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
if (variantCount > 1)
|
||||
{
|
||||
var cultureErrors = ModelState.GetCulturesWithPropertyErrors(Services.LocalizationService);
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService);
|
||||
foreach (var c in contentItem.Variants.Where(x => x.Save && !cultureErrors.Contains(x.Culture)).Select(x => x.Culture).ToArray())
|
||||
{
|
||||
AddSuccessNotification(notifications, c,
|
||||
@@ -1142,16 +1142,19 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var mandatoryCultures = _allLangs.Value.Values.Where(x => x.IsMandatory).Select(x => x.IsoCode).ToList();
|
||||
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService);
|
||||
|
||||
//validate if we can publish based on the mandatory language requirements
|
||||
var canPublish = ValidatePublishingMandatoryLanguages(
|
||||
contentItem, cultureVariants, mandatoryCultures, "speechBubbles/contentReqCulturePublishError",
|
||||
mandatoryVariant => mandatoryVariant.Publish, out var _);
|
||||
cultureErrors,
|
||||
contentItem, cultureVariants, mandatoryCultures,
|
||||
mandatoryVariant => mandatoryVariant.Publish);
|
||||
|
||||
//Now check if there are validation errors on each variant.
|
||||
//If validation errors are detected on a variant and it's state is set to 'publish', then we
|
||||
//need to change it to 'save'.
|
||||
//It is a requirement that this is performed AFTER ValidatePublishingMandatoryLanguages.
|
||||
var cultureErrors = ModelState.GetCulturesWithPropertyErrors(Services.LocalizationService);
|
||||
|
||||
foreach (var variant in contentItem.Variants)
|
||||
{
|
||||
if (cultureErrors.Contains(variant.Culture))
|
||||
@@ -1211,16 +1214,21 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var mandatoryCultures = _allLangs.Value.Values.Where(x => x.IsMandatory).Select(x => x.IsoCode).ToList();
|
||||
|
||||
//validate if we can publish based on the mandatory language requirements
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService);
|
||||
|
||||
//validate if we can publish based on the mandatory languages selected
|
||||
var canPublish = ValidatePublishingMandatoryLanguages(
|
||||
contentItem, cultureVariants, mandatoryCultures, "speechBubbles/contentReqCulturePublishError",
|
||||
mandatoryVariant => mandatoryVariant.Publish, out var _);
|
||||
cultureErrors,
|
||||
contentItem, cultureVariants, mandatoryCultures,
|
||||
mandatoryVariant => mandatoryVariant.Publish);
|
||||
|
||||
//if none are published and there are validation errors for mandatory cultures, then we can't publish anything
|
||||
|
||||
|
||||
//Now check if there are validation errors on each variant.
|
||||
//If validation errors are detected on a variant and it's state is set to 'publish', then we
|
||||
//need to change it to 'save'.
|
||||
//It is a requirement that this is performed AFTER ValidatePublishingMandatoryLanguages.
|
||||
var cultureErrors = ModelState.GetCulturesWithPropertyErrors(Services.LocalizationService);
|
||||
//It is a requirement that this is performed AFTER ValidatePublishingMandatoryLanguages.
|
||||
foreach (var variant in contentItem.Variants)
|
||||
{
|
||||
if (cultureErrors.Contains(variant.Culture))
|
||||
@@ -1260,23 +1268,21 @@ namespace Umbraco.Web.Editors
|
||||
/// <summary>
|
||||
/// Validate if publishing is possible based on the mandatory language requirements
|
||||
/// </summary>
|
||||
/// <param name="culturesWithValidationErrors"></param>
|
||||
/// <param name="contentItem"></param>
|
||||
/// <param name="cultureVariants"></param>
|
||||
/// <param name="mandatoryCultures"></param>
|
||||
/// <param name="localizationKey"></param>
|
||||
/// <param name="publishingCheck"></param>
|
||||
/// <param name="mandatoryVariants"></param>
|
||||
/// <returns></returns>
|
||||
private bool ValidatePublishingMandatoryLanguages(
|
||||
IReadOnlyCollection<string> culturesWithValidationErrors,
|
||||
ContentItemSave contentItem,
|
||||
IReadOnlyCollection<ContentVariantSave> cultureVariants,
|
||||
IReadOnlyList<string> mandatoryCultures,
|
||||
string localizationKey,
|
||||
Func<ContentVariantSave, bool> publishingCheck,
|
||||
out IReadOnlyList<(ContentVariantSave mandatoryVariant, bool isPublished)> mandatoryVariants)
|
||||
Func<ContentVariantSave, bool> publishingCheck)
|
||||
{
|
||||
var canPublish = true;
|
||||
var result = new List<(ContentVariantSave, bool)>();
|
||||
var result = new List<(ContentVariantSave model, bool publishing, bool isValid)>();
|
||||
|
||||
foreach (var culture in mandatoryCultures)
|
||||
{
|
||||
@@ -1285,18 +1291,39 @@ namespace Umbraco.Web.Editors
|
||||
var mandatoryVariant = cultureVariants.First(x => x.Culture.InvariantEquals(culture));
|
||||
|
||||
var isPublished = contentItem.PersistedContent.Published && contentItem.PersistedContent.IsCulturePublished(culture);
|
||||
result.Add((mandatoryVariant, isPublished));
|
||||
|
||||
var isPublishing = isPublished || publishingCheck(mandatoryVariant);
|
||||
var isValid = !culturesWithValidationErrors.InvariantContains(culture);
|
||||
|
||||
if (isPublished || isPublishing) continue;
|
||||
|
||||
//cannot continue publishing since a required language that is not currently being published isn't published
|
||||
AddCultureValidationError(culture, localizationKey);
|
||||
canPublish = false;
|
||||
result.Add((mandatoryVariant, isPublished || isPublishing, isValid));
|
||||
}
|
||||
|
||||
//iterate over the results by invalid first
|
||||
string firstInvalidMandatoryCulture = null;
|
||||
foreach (var r in result.OrderBy(x => x.isValid))
|
||||
{
|
||||
if (!r.isValid)
|
||||
firstInvalidMandatoryCulture = r.model.Culture;
|
||||
|
||||
if (r.publishing && !r.isValid)
|
||||
{
|
||||
//flagged for publishing but the mandatory culture is invalid
|
||||
AddCultureValidationError(r.model.Culture, "publish/contentPublishedFailedReqCultureValidationError");
|
||||
canPublish = false;
|
||||
}
|
||||
else if (r.publishing && r.isValid && firstInvalidMandatoryCulture != null)
|
||||
{
|
||||
//in this case this culture also cannot be published because another mandatory culture is invalid
|
||||
AddCultureValidationError(r.model.Culture, "publish/contentPublishedFailedReqCultureValidationError", firstInvalidMandatoryCulture);
|
||||
canPublish = false;
|
||||
}
|
||||
else if (!r.publishing)
|
||||
{
|
||||
//cannot continue publishing since a required culture that is not currently being published isn't published
|
||||
AddCultureValidationError(r.model.Culture, "speechBubbles/contentReqCulturePublishError");
|
||||
canPublish = false;
|
||||
}
|
||||
}
|
||||
|
||||
mandatoryVariants = result;
|
||||
return canPublish;
|
||||
}
|
||||
|
||||
@@ -1328,14 +1355,15 @@ namespace Umbraco.Web.Editors
|
||||
/// <summary>
|
||||
/// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="culture">Culture to assign the error to</param>
|
||||
/// <param name="localizationKey"></param>
|
||||
private void AddCultureValidationError(string culture, string localizationKey)
|
||||
/// <param name="cultureToken">
|
||||
/// The culture used in the localization message, null by default which means <see cref="culture"/> will be used.
|
||||
/// </param>
|
||||
private void AddCultureValidationError(string culture, string localizationKey, string cultureToken = null)
|
||||
{
|
||||
var key = "_content_variant_" + culture + "_";
|
||||
if (ModelState.ContainsKey(key)) return;
|
||||
var errMsg = Services.TextService.Localize(localizationKey, new[] { _allLangs.Value[culture].CultureName });
|
||||
ModelState.AddModelError(key, errMsg);
|
||||
var errMsg = Services.TextService.Localize(localizationKey, new[] { cultureToken == null ? _allLangs.Value[culture].CultureName : _allLangs.Value[cultureToken].CultureName });
|
||||
ModelState.AddCultureValidationError(culture, errMsg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1763,7 +1791,7 @@ namespace Umbraco.Web.Editors
|
||||
if (!ModelState.IsValid && display.Variants.Count() > 1)
|
||||
{
|
||||
//Add any culture specific errors here
|
||||
var cultureErrors = ModelState.GetCulturesWithPropertyErrors(Services.LocalizationService);
|
||||
var cultureErrors = ModelState.GetCulturesWithErrors(Services.LocalizationService);
|
||||
|
||||
foreach (var cultureError in cultureErrors)
|
||||
{
|
||||
|
||||
@@ -58,7 +58,21 @@ namespace Umbraco.Web
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of cultures that have property errors
|
||||
/// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs
|
||||
/// </summary>
|
||||
/// <param name="modelState"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="errMsg"></param>
|
||||
internal static void AddCultureValidationError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState,
|
||||
string culture, string errMsg)
|
||||
{
|
||||
var key = "_content_variant_" + culture + "_";
|
||||
if (modelState.ContainsKey(key)) return;
|
||||
modelState.AddModelError(key, errMsg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of cultures that have property validation errors errors
|
||||
/// </summary>
|
||||
/// <param name="modelState"></param>
|
||||
/// <param name="localizationService"></param>
|
||||
@@ -81,6 +95,30 @@ namespace Umbraco.Web
|
||||
return cultureErrors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of cultures that have any validation errors
|
||||
/// </summary>
|
||||
/// <param name="modelState"></param>
|
||||
/// <param name="localizationService"></param>
|
||||
/// <returns></returns>
|
||||
internal static IReadOnlyList<string> GetCulturesWithErrors(this System.Web.Http.ModelBinding.ModelStateDictionary modelState,
|
||||
ILocalizationService localizationService)
|
||||
{
|
||||
var propertyCultureErrors = modelState.GetCulturesWithPropertyErrors(localizationService);
|
||||
|
||||
//now check the other special culture errors that are
|
||||
var genericCultureErrors = modelState.Keys
|
||||
.Where(x => x.StartsWith("_content_variant_") && x.EndsWith("_"))
|
||||
.Select(x => x.TrimStart("_content_variant_").TrimEnd("_"))
|
||||
.Where(x => !x.IsNullOrWhiteSpace())
|
||||
//if it's marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language
|
||||
//so errors for those must show up under the default lang.
|
||||
.Select(x => x == "invariant" ? localizationService.GetDefaultLanguageIsoCode() : x)
|
||||
.Distinct();
|
||||
|
||||
return propertyCultureErrors.Union(genericCultureErrors).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the error to model state correctly for a property so we can use it on the client side.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user