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:
@@ -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>
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a property.
|
||||
/// Adds or update a property.
|
||||
/// </summary>
|
||||
internal new void Add(Property property)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user