Removes the need for .Current in some places, adds TODO notes to test a theory, removes IUmbracoEntity from ISimpleContentType since all tests seem to pass without it and because the SimpleContentTypeMapper doesn't actually use ISimpleContentType i don't think it's needed, we'll see what the tests say.

This commit is contained in:
Shannon
2019-02-07 12:43:11 +11:00
parent 9df0f384af
commit 123daed990
9 changed files with 74 additions and 95 deletions

View File

@@ -5,8 +5,11 @@ namespace Umbraco.Core.Models
/// <summary>
/// Represents a simplified view of a content type.
/// </summary>
public interface ISimpleContentType : IUmbracoEntity
public interface ISimpleContentType
{
int Id { get; }
string Name { get; }
/// <summary>
/// Gets the alias of the content type.
/// </summary>

View File

@@ -93,7 +93,7 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Adds a property.
/// Adds or update a property.
/// </summary>
internal new void Add(Property property)
{

View File

@@ -9,9 +9,6 @@ namespace Umbraco.Core.Models
/// </summary>
public class SimpleContentType : ISimpleContentType
{
private readonly int _id;
private readonly string _name;
/// <summary>
/// Initializes a new instance of the <see cref="SimpleContentType"/> class.
/// </summary>
@@ -39,13 +36,12 @@ namespace Umbraco.Core.Models
{
if (contentType == null) throw new ArgumentNullException(nameof(contentType));
_id = contentType.Id;
Id = contentType.Id;
Alias = contentType.Alias;
Variations = contentType.Variations;
Icon = contentType.Icon;
IsContainer = contentType.IsContainer;
Icon = contentType.Icon;
_name = contentType.Name;
Name = contentType.Name;
AllowedAsRoot = contentType.AllowedAsRoot;
IsElement = contentType.IsElement;
}
@@ -53,12 +49,7 @@ namespace Umbraco.Core.Models
/// <inheritdoc />
public string Alias { get; }
/// <inheritdoc />
public int Id
{
get => _id;
set => throw new NotSupportedException();
}
public int Id { get; }
/// <inheritdoc />
public ITemplate DefaultTemplate { get; }
@@ -71,13 +62,8 @@ namespace Umbraco.Core.Models
/// <inheritdoc />
public bool IsContainer { get; }
/// <inheritdoc />
public string Name
{
get => _name;
set => throw new NotSupportedException();
}
public string Name { get; }
/// <inheritdoc />
public bool AllowedAsRoot { get; }
@@ -120,30 +106,29 @@ namespace Umbraco.Core.Models
// we have to have all this, because we're an IUmbracoEntity, because that is
// required by the query expression visitor / SimpleContentTypeMapper
public object DeepClone() => throw new NotImplementedException();
public Guid Key { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public DateTime CreateDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public DateTime UpdateDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public DateTime? DeleteDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool HasIdentity => throw new NotImplementedException();
public int CreatorId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int ParentId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void SetParent(ITreeEntity parent) => throw new NotImplementedException();
public int Level { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public string Path { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public int SortOrder { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public bool Trashed => throw new NotImplementedException();
public bool IsDirty() => throw new NotImplementedException();
public bool IsPropertyDirty(string propName) => throw new NotImplementedException();
public IEnumerable<string> GetDirtyProperties() => throw new NotImplementedException();
public void ResetDirtyProperties() => throw new NotImplementedException();
public bool WasDirty() => throw new NotImplementedException();
public bool WasPropertyDirty(string propertyName) => throw new NotImplementedException();
public void ResetWereDirtyProperties() => throw new NotImplementedException();
public void ResetDirtyProperties(bool rememberDirty) => throw new NotImplementedException();
public IEnumerable<string> GetWereDirtyProperties() => throw new NotImplementedException();
//string ITreeEntity.Name { get => this.Name; set => throw new NotImplementedException(); }
//int IEntity.Id { get => this.Id; set => throw new NotImplementedException(); }
//bool IEntity.HasIdentity => this.Id != default;
//int ITreeEntity.CreatorId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//int ITreeEntity.ParentId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//int ITreeEntity.Level { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//string ITreeEntity.Path { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//int ITreeEntity.SortOrder { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//bool ITreeEntity.Trashed => throw new NotImplementedException();
//Guid IEntity.Key { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//DateTime IEntity.CreateDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//DateTime IEntity.UpdateDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//DateTime? IEntity.DeleteDate { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
//void ITreeEntity.SetParent(ITreeEntity parent) => throw new NotImplementedException();
//object IDeepCloneable.DeepClone() => throw new NotImplementedException();
//bool IRememberBeingDirty.WasDirty() => throw new NotImplementedException();
//bool IRememberBeingDirty.WasPropertyDirty(string propertyName) => throw new NotImplementedException();
//void IRememberBeingDirty.ResetWereDirtyProperties() => throw new NotImplementedException();
//void IRememberBeingDirty.ResetDirtyProperties(bool rememberDirty) => throw new NotImplementedException();
//IEnumerable<string> IRememberBeingDirty.GetWereDirtyProperties() => throw new NotImplementedException();
//bool ICanBeDirty.IsDirty() => throw new NotImplementedException();
//bool ICanBeDirty.IsPropertyDirty(string propName) => throw new NotImplementedException();
//IEnumerable<string> ICanBeDirty.GetDirtyProperties() => throw new NotImplementedException();
//void ICanBeDirty.ResetDirtyProperties() => throw new NotImplementedException();
}
}

View File

@@ -60,7 +60,7 @@ namespace Umbraco.Core.Persistence.Querying
if (m.Expression != null
&& m.Expression.Type != typeof(T)
&& TypeHelper.IsTypeAssignableFrom<IUmbracoEntity>(m.Expression.Type)
&& TypeHelper.IsTypeAssignableFrom<IUmbracoEntity>(m.Expression.Type) //TODO: Could this just be `IEntity` ? why does it need to be IUmbracoEntity, we aren't even using the reference to that below
&& EndsWithConstant(m) == false)
{
//if this is the case, it means we have a sub expression / nested property access, such as: x.ContentType.Alias == "Test";

View File

@@ -30,6 +30,7 @@ namespace Umbraco.Tests.Models
public class ContentTests : UmbracoTestBase
{
private IContentTypeService _contentTypeService;
private IContentTypeBaseServiceProvider _contentTypeServiceProvider;
protected override void Compose()
{
@@ -56,6 +57,8 @@ namespace Umbraco.Tests.Models
var mediaTypeService = Mock.Of<IMediaTypeService>();
var memberTypeService = Mock.Of<IMemberTypeService>();
Composition.Register(_ => ServiceContext.CreatePartial(dataTypeService: dataTypeService, contentTypeBaseServiceProvider: new ContentTypeBaseServiceProvider(_contentTypeService, mediaTypeService, memberTypeService)));
_contentTypeServiceProvider = new ContentTypeBaseServiceProvider(_contentTypeService, Mock.Of<IMediaTypeService>(), Mock.Of<IMemberTypeService>());
}
[Test]
@@ -541,7 +544,7 @@ namespace Umbraco.Tests.Models
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
// Act
content.PropertyValues(new {title = "This is the new title"});
content.PropertyValues(_contentTypeServiceProvider, new { title = "This is the new title"});
// Assert
Assert.That(content.Properties.Any(), Is.True);

View File

@@ -1574,19 +1574,19 @@ namespace Umbraco.Tests.Services
var contentType = MockedContentTypes.CreateAllTypesContentType("test", "test");
ServiceContext.ContentTypeService.Save(contentType, Constants.Security.SuperUserId);
object obj =
new
{
tags = "[\"Hello\",\"World\"]"
};
var content1 = MockedContent.CreateBasicContent(contentType);
content1.PropertyValues(obj);
content1.PropertyValues(ServiceContext.ContentTypeBaseServices, obj);
content1.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content1, Constants.Security.SuperUserId);
Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content1, userId: 0).Success);
var content2 = MockedContent.CreateBasicContent(contentType);
content2.PropertyValues(obj);
content2.PropertyValues(ServiceContext.ContentTypeBaseServices, obj);
content2.ResetDirtyProperties(false);
ServiceContext.ContentService.Save(content2, Constants.Security.SuperUserId);
Assert.IsTrue(ServiceContext.ContentService.SaveAndPublish(content2, userId: 0).Success);

View File

@@ -3,42 +3,48 @@ using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Umbraco.Tests.Testing
{
public static class ContentBaseExtensions
{
public static void PropertyValues(this IContentBase content, object value, string culture = null, string segment = null)
{
content.PropertyValues(Current.Services.ContentTypeBaseServices, value, culture, segment);
}
/// <summary>
/// Set property values by alias with an anonymous object.
/// </summary>
/// <remarks>Does not support variants.</remarks>
public static void PropertyValues(this IContentBase content, object value, string culture = null, string segment = null)
public static void PropertyValues(this IContentBase content, IContentTypeBaseServiceProvider contentTypeServiceProvider, object value, string culture = null, string segment = null)
{
if (value == null)
throw new Exception("No properties has been passed in");
var contentType = Current.Services.ContentTypeBaseServices.GetContentTypeOf(content);
var propertyInfos = value.GetType().GetProperties();
foreach (var propertyInfo in propertyInfos)
{
//Check if a PropertyType with alias exists thus being a valid property
var propertyType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propertyInfo.Name);
if (propertyType == null)
throw new Exception($"The property alias {propertyInfo.Name} is not valid, because no PropertyType with this alias exists");
//Check if a Property with the alias already exists in the collection thus being updated or inserted
var item = content.Properties.FirstOrDefault(x => x.Alias == propertyInfo.Name);
if (item != null)
if (content.Properties.TryGetValue(propertyInfo.Name, out var property))
{
item.SetValue(propertyInfo.GetValue(value, null), culture, segment);
property.SetValue(propertyInfo.GetValue(value, null), culture, segment);
//Update item with newly added value
content.Properties.Add(item);
content.Properties.Add(property);
}
else
{
//TODO: Will this ever happen?? In theory we don't need to lookup the content type here since we can just check if the content contains properties with the correct name,
// however, i think this may be needed in the case where the content type contains property types that do not exist yet as properties on the
// content item? But can that happen? AFAIK it can't/shouldn't because of how we create content items in ContentBase we do _properties.EnsurePropertyTypes!
var contentType = contentTypeServiceProvider.GetContentTypeOf(content);
var propertyType = contentType.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == propertyInfo.Name);
if (propertyType == null)
throw new Exception($"The property alias {propertyInfo.Name} is not valid, because no PropertyType with this alias exists");
//Create new Property to add to collection
var property = propertyType.CreateProperty();
property = propertyType.CreateProperty();
property.SetValue(propertyInfo.GetValue(value, null), culture, segment);
content.Properties.Add(property);
}

View File

@@ -5,6 +5,7 @@ using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
@@ -17,21 +18,23 @@ namespace Umbraco.Web.Editors.Filters
{
private readonly ILogger _logger;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
private readonly IMemberTypeService _memberTypeService;
public MemberSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor)
public MemberSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MemberTypeService)
{
}
public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor)
public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService)
{
_logger = logger;
_umbracoContextAccessor = umbracoContextAccessor;
_memberTypeService = memberTypeService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var model = (MemberSave)actionContext.ActionArguments["contentItem"];
var contentItemValidator = new MemberValidationHelper(_logger, _umbracoContextAccessor);
var contentItemValidator = new MemberValidationHelper(_logger, _umbracoContextAccessor, _memberTypeService);
//now do each validation step
if (contentItemValidator.ValidateExistingContent(model, actionContext))
if (contentItemValidator.ValidateProperties(model, model, actionContext))

View File

@@ -10,43 +10,22 @@ using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Editors.Filters
{
//internal class ContentValidationHelper : ContentItemValidationHelper<IContent, ContentItemSave>
//{
// public ContentValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
// {
// }
// /// <summary>
// /// Validates that the correct information is in the request for saving a culture variant
// /// </summary>
// /// <param name="postedItem"></param>
// /// <param name="actionContext"></param>
// /// <returns></returns>
// protected override bool ValidateCultureVariant(ContentItemSave postedItem, HttpActionContext actionContext)
// {
// var contentType = postedItem.PersistedContent.GetContentType();
// if (contentType.VariesByCulture() && postedItem.Culture.IsNullOrWhiteSpace())
// {
// //we cannot save a content item that is culture variant if no culture was specified in the request!
// actionContext.Response = actionContext.Request.CreateValidationErrorResponse($"No culture found in request. Cannot save a content item that varies by culture, without a specified culture.");
// return false;
// }
// return true;
// }
//}
/// <summary>
/// Custom validation helper so that we can exclude the Member.StandardPropertyTypeStubs from being validating for existence
/// </summary>
internal class MemberValidationHelper : ContentItemValidationHelper<IMember, MemberSave>
{
public MemberValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
private readonly IMemberTypeService _memberTypeService;
public MemberValidationHelper(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService) : base(logger, umbracoContextAccessor)
{
_memberTypeService = memberTypeService;
}
/// <summary>
@@ -118,7 +97,7 @@ namespace Umbraco.Web.Editors.Filters
//if a sensitive value is being submitted.
if (UmbracoContextAccessor.UmbracoContext.Security.CurrentUser.HasAccessToSensitiveData() == false)
{
var contentType = Current.Services.MemberTypeService.Get(model.PersistedContent.ContentTypeId);
var contentType = _memberTypeService.Get(model.PersistedContent.ContentTypeId);
var sensitiveProperties = contentType
.PropertyTypes.Where(x => contentType.IsSensitiveProperty(x.Alias))
.ToList();