Gets validation working with composition alias conflicts
This commit is contained in:
@@ -3,22 +3,41 @@
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
public class InvalidCompositionException : Exception
|
||||
{
|
||||
public string ContentTypeAlias { get; set; }
|
||||
{
|
||||
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
AddedCompositionAlias = addedCompositionAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
|
||||
public string AddedCompositionAlias { get; set; }
|
||||
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
|
||||
public string PropertyTypeAlias { get; set; }
|
||||
public string ContentTypeAlias { get; private set; }
|
||||
|
||||
public string AddedCompositionAlias { get; private set; }
|
||||
|
||||
public string[] PropertyTypeAliases { get; private set; }
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Format(
|
||||
"InvalidCompositionException - ContentType with alias '{0}' was added as a Compsition to ContentType with alias '{1}', " +
|
||||
"but there was a conflict on the PropertyType alias '{2}'. " +
|
||||
return AddedCompositionAlias.IsNullOrWhiteSpace()
|
||||
? string.Format(
|
||||
"ContentType with alias '{0}' has an invalid composition " +
|
||||
"and there was a conflict on the following PropertyTypes: '{1}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
AddedCompositionAlias, ContentTypeAlias, PropertyTypeAlias);
|
||||
ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
|
||||
: string.Format(
|
||||
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
|
||||
"but there was a conflict on the following PropertyTypes: '{2}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,13 +94,7 @@ namespace Umbraco.Core.Models
|
||||
.Select(p => p.Alias)).ToList();
|
||||
|
||||
if (conflictingPropertyTypeAliases.Any())
|
||||
throw new InvalidCompositionException
|
||||
{
|
||||
AddedCompositionAlias = contentType.Alias,
|
||||
ContentTypeAlias = Alias,
|
||||
PropertyTypeAlias =
|
||||
string.Join(", ", conflictingPropertyTypeAliases)
|
||||
};
|
||||
throw new InvalidCompositionException(Alias, contentType.Alias, conflictingPropertyTypeAliases.ToArray());
|
||||
|
||||
_contentTypeComposition.Add(contentType);
|
||||
OnPropertyChanged(ContentTypeCompositionSelector);
|
||||
|
||||
@@ -308,15 +308,28 @@ namespace Umbraco.Core.Services
|
||||
|
||||
}
|
||||
|
||||
public void Validate(IContentTypeComposition compo)
|
||||
/// <summary>
|
||||
/// Validates the composition, if its invalid a list of property type aliases that were duplicated is returned
|
||||
/// </summary>
|
||||
/// <param name="compo"></param>
|
||||
/// <returns></returns>
|
||||
public Attempt<string[]> ValidateComposition(IContentTypeComposition compo)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
ValidateLocked(compo);
|
||||
try
|
||||
{
|
||||
ValidateLocked(compo);
|
||||
return Attempt<string[]>.Succeed();
|
||||
}
|
||||
catch (InvalidCompositionException ex)
|
||||
{
|
||||
return Attempt.Fail(ex.PropertyTypeAliases, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateLocked(IContentTypeComposition compositionContentType)
|
||||
protected void ValidateLocked(IContentTypeComposition compositionContentType)
|
||||
{
|
||||
// performs business-level validation of the composition
|
||||
// should ensure that it is absolutely safe to save the composition
|
||||
@@ -369,10 +382,8 @@ namespace Umbraco.Core.Services
|
||||
if (contentTypeDependency == null) continue;
|
||||
var intersect = contentTypeDependency.PropertyTypes.Select(x => x.Alias.ToLowerInvariant()).Intersect(propertyTypeAliases).ToArray();
|
||||
if (intersect.Length == 0) continue;
|
||||
|
||||
var message = string.Format("The following PropertyType aliases from the current ContentType conflict with existing PropertyType aliases: {0}.",
|
||||
string.Join(", ", intersect));
|
||||
throw new Exception(message);
|
||||
|
||||
throw new InvalidCompositionException(compositionContentType.Alias, intersect.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IContentTypeService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the composition, if its invalid a list of property type aliases that were duplicated is returned
|
||||
/// </summary>
|
||||
/// <param name="compo"></param>
|
||||
/// <returns></returns>
|
||||
Attempt<string[]> ValidateComposition(IContentTypeComposition compo);
|
||||
|
||||
Attempt<int> CreateFolder(int parentId, string name, int userId = 0);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -508,7 +508,7 @@ function umbDataFormatter() {
|
||||
|
||||
var realProperties = _.reject(g.properties, function (p) {
|
||||
//do not include these properties
|
||||
return p.propertyState === "init";
|
||||
return p.propertyState === "init" || p.inherited === true;
|
||||
});
|
||||
|
||||
var saveProperties = _.map(realProperties, function (p) {
|
||||
@@ -518,9 +518,19 @@ function umbDataFormatter() {
|
||||
|
||||
saveGroup.properties = saveProperties;
|
||||
|
||||
//if this is an inherited group and there are not non-inherited properties on it, then don't send up the data
|
||||
if (saveGroup.inherited === true && saveProperties.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return saveGroup;
|
||||
});
|
||||
|
||||
//we don't want any null groups
|
||||
saveModel.groups = _.reject(saveModel.groups, function(g) {
|
||||
return !g;
|
||||
});
|
||||
|
||||
return saveModel;
|
||||
},
|
||||
|
||||
|
||||
@@ -96,10 +96,12 @@
|
||||
<ng-form name="propertyTypeForm">
|
||||
<div class="control-group -no-margin" ng-if="!sortingMode">
|
||||
|
||||
<umb-locked-field locked="locked"
|
||||
<div class="umb-group-builder__property-meta-alias" ng-if="property.inherited">{{ property.alias }}</div>
|
||||
<umb-locked-field ng-if="!property.inherited"
|
||||
locked="locked"
|
||||
ng-model="property.alias"
|
||||
placeholder-text="'Alias...'"
|
||||
server-validation-field="{{'Groups[' + $parent.$index + '].Properties[' + $index + '].Alias'}}">
|
||||
server-validation-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Alias'}}">
|
||||
</umb-locked-field>
|
||||
|
||||
<div class="umb-group-builder__property-meta-label">
|
||||
@@ -107,7 +109,7 @@
|
||||
name="groupName"
|
||||
umb-auto-resize
|
||||
required
|
||||
val-server-field="{{'Groups[' + $parent.$index + '].Properties[' + $index + '].Label'}}"></textarea>
|
||||
val-server-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Label'}}"></textarea>
|
||||
|
||||
<span class="help-inline" val-msg-for="groupName" val-toggle-msg="valServerField"></span>
|
||||
</div>
|
||||
|
||||
@@ -89,7 +89,42 @@ namespace Umbraco.Web.Editors
|
||||
? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id
|
||||
: Request.CreateValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors
|
||||
/// </summary>
|
||||
/// <param name="contentTypeSave"></param>
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
private void ValidateComposition(ContentTypeSave contentTypeSave, IContentTypeComposition composition)
|
||||
{
|
||||
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
|
||||
if (validateAttempt == false)
|
||||
{
|
||||
//if it's not successful then we need to return some model state for the property aliases that
|
||||
// are duplicated
|
||||
var propertyAliases = validateAttempt.Result.Distinct();
|
||||
foreach (var propertyAlias in propertyAliases)
|
||||
{
|
||||
//find the property relating to these
|
||||
var prop = contentTypeSave.Groups.SelectMany(x => x.Properties).Single(x => x.Alias == propertyAlias);
|
||||
var group = contentTypeSave.Groups.Single(x => x.Properties.Contains(prop));
|
||||
var propIndex = group.Properties.IndexOf(prop);
|
||||
var groupIndex = contentTypeSave.Groups.IndexOf(group);
|
||||
|
||||
var key = string.Format("Groups[{0}].Properties[{1}].Alias", groupIndex, propIndex);
|
||||
ModelState.AddModelError(key, "Duplicate property aliases not allowed between compositions");
|
||||
}
|
||||
|
||||
var display = Mapper.Map<ContentTypeDisplay>(composition);
|
||||
//map the 'save' data on top
|
||||
display = Mapper.Map(contentTypeSave, display);
|
||||
display.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(display));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ContentTypeDisplay PostSave(ContentTypeSave contentTypeSave)
|
||||
{
|
||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
||||
@@ -106,8 +141,6 @@ namespace Umbraco.Web.Editors
|
||||
forDisplay.Errors = ModelState.ToErrorDictionary();
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
|
||||
}
|
||||
|
||||
//TODO: Deal with validation for composition with property and group names/aliases
|
||||
|
||||
//filter out empty properties
|
||||
contentTypeSave.Groups = contentTypeSave.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
|
||||
@@ -129,8 +162,11 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
Mapper.Map(contentTypeSave, found);
|
||||
ctService.Save(found);
|
||||
|
||||
//NOTE: this throws an error response if it is not valid
|
||||
ValidateComposition(contentTypeSave, found);
|
||||
|
||||
ctService.Save(found);
|
||||
display = Mapper.Map<ContentTypeDisplay>(found);
|
||||
}
|
||||
else
|
||||
@@ -166,11 +202,16 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//save as new
|
||||
var newCt = Mapper.Map<IContentType>(contentTypeSave);
|
||||
|
||||
//NOTE: this throws an error response if it is not valid
|
||||
ValidateComposition(contentTypeSave, newCt);
|
||||
|
||||
ctService.Save(newCt);
|
||||
|
||||
//we need to save it twice to allow itself under itself.
|
||||
if (allowItselfAsChild)
|
||||
{
|
||||
//NOTE: This will throw if the composition isn't right... but it shouldn't be at this stage
|
||||
newCt.AddContentType(newCt);
|
||||
ctService.Save(newCt);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user