Fixes merge conflicts

This commit is contained in:
Bjarke Berg
2020-08-07 14:00:56 +02:00
237 changed files with 29451 additions and 4202 deletions

View File

@@ -348,6 +348,10 @@ namespace Umbraco.Web.BackOffice.Controllers
"imageUrlGeneratorApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ImageUrlGeneratorController>(
controller => controller.GetCropUrl(null, null, null, null, null))
},
{
"elementTypeApiBaseUrl", _linkGenerator.GetUmbracoApiServiceBaseUrl<ElementTypeController>(
controller => controller.GetAll())
},
}
},
{

View File

@@ -405,6 +405,29 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return GetEmpty(contentType, parentId);
}
/// <summary>
/// Gets an empty content item for the document type.
/// </summary>
/// <param name="contentTypeKey"></param>
/// <param name="parentId"></param>
[TypeFilter(typeof(OutgoingEditorModelEventAttribute))]
public ContentItemDisplay GetEmptyByKey(Guid contentTypeKey, int parentId)
{
var contentType = _contentTypeService.Get(contentTypeKey);
if (contentType == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return GetEmpty(contentType, parentId);
}
private ContentItemDisplay GetEmpty(IContentType contentType, int parentId)
{
var emptyContent = _contentService.Create("", parentId, contentType.Alias, _webSecurity.GetUserId().ResultOr(0));
var mapped = MapToDisplay(emptyContent);
// translate the content type name if applicable

View File

@@ -0,0 +1,36 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Core.Services;
using Umbraco.Web.Common.Attributes;
namespace Umbraco.Web.BackOffice.Controllers
{
[PluginController("UmbracoApi")]
public class ElementTypeController : UmbracoAuthorizedJsonController
{
private readonly IContentTypeService _contentTypeService;
public ElementTypeController(IContentTypeService contentTypeService)
{
_contentTypeService = contentTypeService;
}
[HttpGet]
public IEnumerable<object> GetAll()
{
return _contentTypeService
.GetAllElementTypes()
.OrderBy(x => x.SortOrder)
.Select(x => new
{
id = x.Id,
key = x.Key,
name = x.Name,
description = x.Description,
alias = x.Alias,
icon = x.Icon
});
}
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Core;
using Umbraco.Web.PropertyEditors.Validation;
namespace Umbraco.Extensions
{
@@ -22,7 +23,28 @@ namespace Umbraco.Extensions
}
/// <summary>
/// Adds the error to model state correctly for a property so we can use it on the client side.
/// Adds the <see cref="ValidationResult"/> to the model state with the appropriate keys for property errors
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
/// <param name="propertyAlias"></param>
/// <param name="culture"></param>
/// <param name="segment"></param>
internal static void AddPropertyValidationError(this ModelStateDictionary modelState,
ValidationResult result, string propertyAlias, string culture = "", string segment = "")
{
modelState.AddValidationError(
result,
"_Properties",
propertyAlias,
//if the culture is null, we'll add the term 'invariant' as part of the key
culture.IsNullOrWhiteSpace() ? "invariant" : culture,
// if the segment is null, we'll add the term 'null' as part of the key
segment.IsNullOrWhiteSpace() ? "null" : segment);
}
/// <summary>
/// Adds an <see cref="ContentPropertyValidationResult"/> error to model state for a property so we can use it on the client side.
/// </summary>
/// <param name="modelState"></param>
/// <param name="result"></param>
@@ -31,15 +53,10 @@ namespace Umbraco.Extensions
internal static void AddPropertyError(this ModelStateDictionary modelState,
ValidationResult result, string propertyAlias, string culture = "", string segment = "")
{
if (culture == null)
culture = "";
modelState.AddValidationError(result, "_Properties", propertyAlias,
//if the culture is null, we'll add the term 'invariant' as part of the key
culture.IsNullOrWhiteSpace() ? "invariant" : culture,
// if the segment is null, we'll add the term 'null' as part of the key
segment.IsNullOrWhiteSpace() ? "null" : segment);
modelState.AddPropertyValidationError(new ContentPropertyValidationResult(result, culture, segment), propertyAlias, culture, segment);
}
/// <summary>
/// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs
/// </summary>
@@ -151,7 +168,7 @@ namespace Umbraco.Extensions
}
if (!withNames)
{
modelState.TryAddModelError($"{delimitedParts}", result.ErrorMessage);
modelState.TryAddModelError($"{delimitedParts}", result.ToString());
}
}

View File

@@ -1,5 +1,7 @@
using System;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
@@ -19,13 +21,16 @@ namespace Umbraco.Web.BackOffice.Filters
/// </summary>
internal abstract class ContentModelValidator
{
protected IWebSecurity WebSecurity { get; }
public IPropertyValidationService PropertyValidationService { get; }
protected ILogger Logger { get; }
protected ContentModelValidator(ILogger logger, IWebSecurity webSecurity)
protected ContentModelValidator(ILogger logger, IWebSecurity webSecurity, IPropertyValidationService propertyValidationService)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
WebSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity));
PropertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService));
}
}
@@ -46,7 +51,12 @@ namespace Umbraco.Web.BackOffice.Filters
{
private readonly ILocalizedTextService _textService;
protected ContentModelValidator(ILogger logger, IWebSecurity webSecurity, ILocalizedTextService textService) : base(logger, webSecurity)
protected ContentModelValidator(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IPropertyValidationService propertyValidationService)
: base(logger, webSecurity, propertyValidationService)
{
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
}
@@ -126,18 +136,6 @@ namespace Umbraco.Web.BackOffice.Filters
{
var properties = modelWithProperties.Properties.ToDictionary(x => x.Alias, x => x);
// Retrieve default messages used for required and regex validatation. We'll replace these
// if set with custom ones if they've been provided for a given property.
var requiredDefaultMessages = new[]
{
_textService.Localize("validation", "invalidNull"),
_textService.Localize("validation", "invalidEmpty")
};
var formatDefaultMessages = new[]
{
_textService.Localize("validation", "invalidPattern"),
};
foreach (var p in dto.Properties)
{
var editor = p.PropertyEditor;
@@ -157,7 +155,7 @@ namespace Umbraco.Web.BackOffice.Filters
var postedValue = postedProp.Value;
ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState, requiredDefaultMessages, formatDefaultMessages);
ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState);
}
@@ -181,26 +179,29 @@ namespace Umbraco.Web.BackOffice.Filters
IDataEditor editor,
ContentPropertyDto property,
object postedValue,
ModelStateDictionary modelState,
string[] requiredDefaultMessages,
string[] formatDefaultMessages)
ModelStateDictionary modelState)
{
var valueEditor = editor.GetValueEditor(property.DataType.Configuration);
foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp))
if (property is null) throw new ArgumentNullException(nameof(property));
if (property.DataType is null) throw new InvalidOperationException($"{nameof(property)}.{nameof(property.DataType)} cannot be null");
foreach (var validationResult in PropertyValidationService.ValidatePropertyValue(
editor, property.DataType, postedValue, property.IsRequired,
property.ValidationRegExp, property.IsRequiredMessage, property.ValidationRegExpMessage))
{
// If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate().
if (property.IsRequired && !string.IsNullOrWhiteSpace(property.IsRequiredMessage) && requiredDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase))
{
r.ErrorMessage = property.IsRequiredMessage;
}
if (!string.IsNullOrWhiteSpace(property.ValidationRegExp) && !string.IsNullOrWhiteSpace(property.ValidationRegExpMessage) && formatDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase))
{
r.ErrorMessage = property.ValidationRegExpMessage;
}
modelState.AddPropertyError(r, property.Alias, property.Culture, property.Segment);
AddPropertyError(model, modelWithProperties, editor, property, validationResult, modelState);
}
}
protected virtual void AddPropertyError(
TModelSave model,
TModelWithProperties modelWithProperties,
IDataEditor editor,
ContentPropertyDto property,
ValidationResult validationResult,
ModelStateDictionary modelState)
{
modelState.AddPropertyError(validationResult, property.Alias, property.Culture, property.Segment);
}
}
}

View File

@@ -11,8 +11,14 @@ namespace Umbraco.Web.BackOffice.Filters
/// </summary>
internal class ContentSaveModelValidator : ContentModelValidator<IContent, ContentItemSave, ContentVariantSave>
{
public ContentSaveModelValidator(ILogger logger, IWebSecurity webSecurity, ILocalizedTextService textService) : base(logger, webSecurity, textService)
public ContentSaveModelValidator(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IPropertyValidationService propertyValidationService)
: base(logger, webSecurity, textService, propertyValidationService)
{
}
}
}

View File

@@ -31,15 +31,21 @@ namespace Umbraco.Web.BackOffice.Filters
{
private readonly IContentService _contentService;
private readonly IEntityService _entityService;
private readonly IPropertyValidationService _propertyValidationService;
private readonly ILogger _logger;
private readonly ILocalizedTextService _textService;
private readonly IUserService _userService;
private readonly IWebSecurity _webSecurity;
public ContentSaveValidationFilter(ILogger logger, IWebSecurity webSecurity,
ILocalizedTextService textService, IContentService contentService, IUserService userService,
IEntityService entityService)
public ContentSaveValidationFilter(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IContentService contentService,
IUserService userService,
IEntityService entityService,
IPropertyValidationService propertyValidationService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity));
@@ -47,12 +53,13 @@ namespace Umbraco.Web.BackOffice.Filters
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
_propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService));
}
public void OnActionExecuting(ActionExecutingContext context)
{
var model = (ContentItemSave) context.ActionArguments["contentItem"];
var contentItemValidator = new ContentSaveModelValidator(_logger, _webSecurity, _textService);
var contentItemValidator = new ContentSaveModelValidator(_logger, _webSecurity, _textService, _propertyValidationService);
if (!ValidateAtLeastOneVariantIsBeingSaved(model, context)) return;
if (!contentItemValidator.ValidateExistingContent(model, context)) return;

View File

@@ -23,6 +23,7 @@ namespace Umbraco.Web.BackOffice.Filters
private sealed class MediaItemSaveValidationFilter : IActionFilter
{
private readonly IEntityService _entityService;
private readonly IPropertyValidationService _propertyValidationService;
private readonly ILogger _logger;
@@ -30,20 +31,26 @@ namespace Umbraco.Web.BackOffice.Filters
private readonly ILocalizedTextService _textService;
private readonly IWebSecurity _webSecurity;
public MediaItemSaveValidationFilter(ILogger logger, IWebSecurity webSecurity,
ILocalizedTextService textService, IMediaService mediaService, IEntityService entityService)
public MediaItemSaveValidationFilter(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IMediaService mediaService,
IEntityService entityService,
IPropertyValidationService propertyValidationService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity));
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
_mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
_propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService));
}
public void OnActionExecuting(ActionExecutingContext context)
{
var model = (MediaItemSave) context.ActionArguments["contentItem"];
var contentItemValidator = new MediaSaveModelValidator(_logger, _webSecurity, _textService);
var contentItemValidator = new MediaSaveModelValidator(_logger, _webSecurity, _textService, _propertyValidationService);
if (ValidateUserAccess(model, context))
{

View File

@@ -11,7 +11,12 @@ namespace Umbraco.Web.BackOffice.Filters
/// </summary>
internal class MediaSaveModelValidator : ContentModelValidator<IMedia, MediaItemSave, IContentProperties<ContentPropertyBasic>>
{
public MediaSaveModelValidator(ILogger logger, IWebSecurity webSecurity, ILocalizedTextService textService) : base(logger, webSecurity, textService)
public MediaSaveModelValidator(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IPropertyValidationService propertyValidationService)
: base(logger, webSecurity, textService, propertyValidationService)
{
}
}

View File

@@ -30,8 +30,9 @@ namespace Umbraco.Web.BackOffice.Filters
ILocalizedTextService textService,
IMemberTypeService memberTypeService,
IMemberService memberService,
IShortStringHelper shortStringHelper)
: base(logger, webSecurity, textService)
IShortStringHelper shortStringHelper,
IPropertyValidationService propertyValidationService)
: base(logger, webSecurity, textService, propertyValidationService)
{
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));

View File

@@ -27,8 +27,16 @@ namespace Umbraco.Web.BackOffice.Filters
private readonly IMemberTypeService _memberTypeService;
private readonly IMemberService _memberService;
private readonly IShortStringHelper _shortStringHelper;
private readonly IPropertyValidationService _propertyValidationService;
public MemberSaveValidationFilter(ILogger logger, IWebSecurity webSecurity, ILocalizedTextService textService, IMemberTypeService memberTypeService, IMemberService memberService, IShortStringHelper shortStringHelper)
public MemberSaveValidationFilter(
ILogger logger,
IWebSecurity webSecurity,
ILocalizedTextService textService,
IMemberTypeService memberTypeService,
IMemberService memberService,
IShortStringHelper shortStringHelper,
IPropertyValidationService propertyValidationService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_webSecurity = webSecurity ?? throw new ArgumentNullException(nameof(webSecurity));
@@ -36,12 +44,13 @@ namespace Umbraco.Web.BackOffice.Filters
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
_memberService = memberService ?? throw new ArgumentNullException(nameof(memberService));
_shortStringHelper = shortStringHelper ?? throw new ArgumentNullException(nameof(shortStringHelper));
_propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService));
}
public void OnActionExecuting(ActionExecutingContext context)
{
var model = (MemberSave)context.ActionArguments["contentItem"];
var contentItemValidator = new MemberSaveModelValidator(_logger, _webSecurity, _textService, _memberTypeService, _memberService, _shortStringHelper);
var contentItemValidator = new MemberSaveModelValidator(_logger, _webSecurity, _textService, _memberTypeService, _memberService, _shortStringHelper, _propertyValidationService);
//now do each validation step
if (contentItemValidator.ValidateExistingContent(model, context))
if (contentItemValidator.ValidateProperties(model, model, context))

View File

@@ -90,6 +90,7 @@ namespace Umbraco.Web.Models.Mapping
target.AllowedTemplates = GetAllowedTemplates(source);
target.ContentApps = _commonMapper.GetContentApps(source);
target.ContentTypeId = source.ContentType.Id;
target.ContentTypeKey = source.ContentType.Key;
target.ContentTypeAlias = source.ContentType.Alias;
target.ContentTypeName = _localizedTextService.UmbracoDictionaryTranslate(_cultureDictionary, source.ContentType.Name);
target.DocumentType = _commonMapper.GetContentType(source, context);

View File

@@ -74,7 +74,15 @@ namespace Umbraco.Web.BackOffice.ModelBinders
return;
}
model.PersistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
var persistedContent = ContentControllerBase.IsCreatingAction(model.Action) ? CreateNew(model) : GetExisting(model);
BindModel(model, persistedContent, _modelBinderHelper, _umbracoMapper);
bindingContext.Result = ModelBindingResult.Success(model);
}
internal static void BindModel(ContentItemSave model, IContent persistedContent, ContentModelBinderHelper modelBinderHelper, UmbracoMapper umbracoMapper)
{
model.PersistedContent =persistedContent;
//create the dto from the persisted model
if (model.PersistedContent != null)
@@ -82,7 +90,7 @@ namespace Umbraco.Web.BackOffice.ModelBinders
foreach (var variant in model.Variants)
{
//map the property dto collection with the culture of the current variant
variant.PropertyCollectionDto = _umbracoMapper.Map<ContentPropertyCollectionDto>(
variant.PropertyCollectionDto = umbracoMapper.Map<ContentPropertyCollectionDto>(
model.PersistedContent,
context =>
{
@@ -92,13 +100,9 @@ namespace Umbraco.Web.BackOffice.ModelBinders
});
//now map all of the saved values to the dto
_modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto);
modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto);
}
}
bindingContext.Result = ModelBindingResult.Success(model);
}
}
}

View File

@@ -0,0 +1,50 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
namespace Umbraco.Web.PropertyEditors.Validation
{
/// <summary>
/// Custom <see cref="ValidationResult"/> for content properties
/// </summary>
/// <remarks>
/// This clones the original result and then ensures the nested result if it's the correct type.
///
/// For a more indepth explanation of how server side validation works with the angular app, see this GitHub PR:
/// https://github.com/umbraco/Umbraco-CMS/pull/8339
/// </remarks>
public class ContentPropertyValidationResult : ValidationResult
{
private readonly string _culture;
private readonly string _segment;
public ContentPropertyValidationResult(ValidationResult nested, string culture, string segment)
: base(nested.ErrorMessage, nested.MemberNames)
{
ComplexEditorResults = nested as ComplexEditorValidationResult;
_culture = culture;
_segment = segment;
}
/// <summary>
/// Nested validation results for the content property
/// </summary>
/// <remarks>
/// There can be nested results for complex editors that contain other editors
/// </remarks>
public ComplexEditorValidationResult ComplexEditorResults { get; }
/// <summary>
/// Return the <see cref="ValidationResult.ErrorMessage"/> if <see cref="ComplexEditorResults"/> is null, else the serialized
/// complex validation results
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (ComplexEditorResults == null)
return base.ToString();
var json = JsonConvert.SerializeObject(this, new ValidationResultConverter(_culture, _segment));
return json;
}
}
}

View File

@@ -0,0 +1,156 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Umbraco.Core;
using Umbraco.Extensions;
namespace Umbraco.Web.PropertyEditors.Validation
{
/// <summary>
/// Custom json converter for <see cref="ValidationResult"/> and <see cref="ContentPropertyValidationResult"/>
/// </summary>
/// <remarks>
/// This converter is specifically used to convert validation results for content in order to be able to have nested
/// validation results for complex editors.
///
/// For a more indepth explanation of how server side validation works with the angular app, see this GitHub PR:
/// https://github.com/umbraco/Umbraco-CMS/pull/8339
/// </remarks>
internal class ValidationResultConverter : JsonConverter
{
private readonly string _culture;
private readonly string _segment;
/// <summary>
/// Constructor
/// </summary>
/// <param name="culture">The culture of the containing property which will be transfered to all child model state</param>
/// <param name="segment">The segment of the containing property which will be transfered to all child model state</param>
public ValidationResultConverter(string culture = "", string segment = "")
{
_culture = culture;
_segment = segment;
}
public override bool CanConvert(Type objectType) => typeof(ValidationResult).IsAssignableFrom(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var camelCaseSerializer = new JsonSerializer
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
DefaultValueHandling = DefaultValueHandling.Ignore
};
foreach (var c in serializer.Converters)
camelCaseSerializer.Converters.Add(c);
var validationResult = (ValidationResult)value;
if (validationResult is ComplexEditorValidationResult nestedResult && nestedResult.ValidationResults.Count > 0)
{
var ja = new JArray();
foreach(var nested in nestedResult.ValidationResults)
{
// recurse to write out the ComplexEditorElementTypeValidationResult
var block = JObject.FromObject(nested, camelCaseSerializer);
ja.Add(block);
}
if (nestedResult.ValidationResults.Count > 0)
{
ja.WriteTo(writer);
}
}
else if (validationResult is ComplexEditorElementTypeValidationResult elementTypeValidationResult && elementTypeValidationResult.ValidationResults.Count > 0)
{
var joElementType = new JObject
{
{ "$id", elementTypeValidationResult.BlockId },
// We don't use this anywhere, though it's nice for debugging
{ "$elementTypeAlias", elementTypeValidationResult.ElementTypeAlias }
};
var modelState = new ModelStateDictionary();
// loop over property validations
foreach (var propTypeResult in elementTypeValidationResult.ValidationResults)
{
// group the results by their type and iterate the groups
foreach (var result in propTypeResult.ValidationResults.GroupBy(x => x.GetType()))
{
// if the group's type isn't ComplexEditorValidationResult then it will in 99% of cases be
// just ValidationResult for whcih we want to create the sub "ModelState" data. If it's not a normal
// ValidationResult it will still just be converted to normal ModelState.
if (result.Key == typeof(ComplexEditorValidationResult))
{
// if it's ComplexEditorValidationResult then there can only be one which is validated so just get the single
if (result.Any())
{
var complexResult = result.Single();
// recurse to get the validation result object
var obj = JToken.FromObject(complexResult, camelCaseSerializer);
joElementType.Add(propTypeResult.PropertyTypeAlias, obj);
// For any nested property error we add the model state as empty state for that nested property
// NOTE: Instead of the empty validation message we could put in the translated
// "errors/propertyHasErrors" message, however I think that leaves for less flexibility since it could/should be
// up to the front-end validator to show whatever message it wants (if any) for an error indicating a nested property error.
// Will leave blank.
modelState.AddPropertyValidationError(new ValidationResult(string.Empty), propTypeResult.PropertyTypeAlias, _culture, _segment);
}
}
else
{
foreach (var v in result)
{
modelState.AddPropertyValidationError(v, propTypeResult.PropertyTypeAlias, _culture, _segment);
}
}
}
}
if (modelState.Count > 0)
{
joElementType.Add("ModelState", JObject.FromObject(modelState.ToErrorDictionary()));
}
joElementType.WriteTo(writer);
}
else
{
if (validationResult is ContentPropertyValidationResult propertyValidationResult
&& propertyValidationResult.ComplexEditorResults?.ValidationResults.Count > 0)
{
// recurse to write out the NestedValidationResults
var obj = JToken.FromObject(propertyValidationResult.ComplexEditorResults, camelCaseSerializer);
obj.WriteTo(writer);
}
var jo = new JObject();
if (!validationResult.ErrorMessage.IsNullOrWhiteSpace())
{
var errObj = JToken.FromObject(validationResult.ErrorMessage, camelCaseSerializer);
jo.Add("errorMessage", errObj);
}
if (validationResult.MemberNames.Any())
{
var memberNamesObj = JToken.FromObject(validationResult.MemberNames, camelCaseSerializer);
jo.Add("memberNames", memberNamesObj);
}
if (jo.HasValues)
jo.WriteTo(writer);
}
}
}
}

View File

@@ -24,6 +24,9 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.UnitTests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Umbraco.Tests.Integration</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>